Redis - 高可用架构

243

一、哨兵机制

1.1 功能

哨兵是 Redis 集群机构中非常重要的一个组件,主要有以下功能:

  • 集群监控:负责监控 Master 和 Slave 进程是否正常工作
  • 消息通知:如果某个 Redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员
  • 故障转移:如果 Master 挂了,会自动转移到 Salve 上
  • 配置中心:如果发生主备切换,通知 Client 客户端新的 Master 地址

1.2 作用

哨兵用于实现 Redis 集群的高可用,本身也是分布式的,作为一个哨兵集群去运行,互相协同工作

主备切换时,判断一个 Master 是否宕机,需要大部分哨兵都同意,达成 master 是客观宕机才行,接着还需要哨兵进行授权,只有大多数哨兵进行授权了,才能正式进行主备切换

这里哨兵节点挂了,哨兵集群要保证能正常工作,因为如果作为一个高可用架构的重要组成部分,它是单点的,那就根本无法保证架构高可用了

二、哨兵切换

2.1 部署注意点

  1. 哨兵至少需要 3 个实例,来保证自己的健壮性
  2. 哨兵 + Redis 主从的部署架构,无法保证数据零丢失,只能保证 Redis 集群的高可用
  3. 对于哨兵 + Redis 主从这种复杂的部署架构,尽量在测试环境和生产环境都进行充足的测试和演练

2.2 主备切换

2.2.1 过程

在 Redis 里面,主备切换这个过程有两个:

  1. 故障判断:需要一定数量的哨兵进行选举,判断故障(客观宕机)
  2. 授权切换:需要一定数量的哨兵进行授权,从而进行主备切换

哨兵的故障判断,通过 quorum 参数来调整:

通过设置 quorum 这个参数,这个参数的意义理解为 “法定人数”,也就是说,只有哨兵集群存在大于或等于 quorum 数量的哨兵都认为 master 挂掉了,才会做一个选举,选举出一个哨兵去做故障转移

选举出了一个哨兵后,就要进行主备切换了,而进行主备切换,还需要进行授权

哨兵的授权切换,通过 majority 参数来调整:

通过设置 majority 这个参数,这个参数的意义理解为 “投票数”,也就是说,只有哨兵集群存在大于或等于 majority 数量的哨兵认同可以授权,才能正式进行主从切换

从中可以看出,进行一次主备切换:

  • 如果 quorum < majority ,那么大于或等于 majority 的哨兵授权即可执行主备切换
  • 如果 quorum >= majority,那么大于或等于 quorum 的哨兵都授权才能进行主备切换

2.2.2 举例

2 节点哨兵集群:

四个进程如下,每个节点对应一个 Redis 普通进程和哨兵进程,Redis 进程为 MasterRedis-Slave

+----+         +----+
| M1 |---------| R1 |
| S1 |         | S2 |
+----+         +----+
  • 配置 quorum = 1,master 宕机,只需要 S1 / S2 一个哨兵认为 master 宕机,就可以进行切换
  • 同时 S1 / S2 会进行选举,选举一个哨兵,执行故障转移
  • 配置 majority = 2,至于为什么是 2 ,规定 majority = num(sentinels) / 2 + 1,这样表明大多数哨兵是运行的,所以 majority 只能大于等于这个数
  • 如果 M1 进程宕机,哨兵 S1 正常运行,那么故障转移是 OK 的
  • 如果 M1S1所在的主机宕机了,那么哨兵只剩一个,此时就没有 majority 的数量来运行执行故障转移了

经典的 3 节点哨兵集群:

六个进程如下,每个节点对应一个 Redis 普通进程和哨兵进程,Redis 进程为 MasterRedis-Slave

       +----+
       | M1 |
       | S1 |
       +----+
          |
+----+    |    +----+
| R2 |----+----| R3 |
| S2 |         | S3 |
+----+         +----+
  • 配置 quorum = 2,master 所在节点宕机,也就是 M1 的那个节点,那么三个哨兵剩下两个
  • S2S3 可以一致认为 master 宕机,选举出一个执行故障转移
  • 配置 majority = 2(上面有公式),剩下两个哨兵运行着,可以顺利进行故障转移

2.3 数据丢失

2.3.1 异步复制

主备切换过程中,异步复制可能导致数据丢失,因为 master - > slave 的复制是异步的,可能存在部分数据还没复制到 slave,master 就宕机了,此时这部分数据就丢失了

所以这样的情况下的 slave 被选举出来当 master ,数据是不完整的

2.3.2 脑裂

某个 master 所在机器突然脱离了正常的网络,跟其他 slave 机器不能连接,但是实际上 master 还运行着,此时哨兵可能开启选举,将其他 slave 切换成了 master

于是就会发现,此时的集群里面就会有两个 master,这就是脑裂现象

所以,当某个 slave 被切换成了 master,可能发送这种情况:

  • client 还没来得及切换到新的 master,还继续向旧的 master 写数据
  • 旧 maser 再次恢复,作为一个 slave 挂到新的 master 上,自己的数据清空
  • 该 slave 从新 master 复制数据,但新的 master 并没有后来 client 写入的数据
  • 于是,部分数据丢失

2.3.3 解决方案

减少复制丢失

数据丢失不能完全避免,只能尽量减少,异步复制过程,具体是通过配置两个参数来调整

min-slaves-to-write 1
min-slaves-max-lag 10

这个例子,标识至少有 1 个 slave,数据复制和同步的延迟不能超过 10 秒

那么一旦所有的 slave,数据复制和同步延迟都超过了 10 秒,那么这个时候,master 就不会再接收任何请求

减少脑裂丢失

脑裂数据丢失也一样,只能尽量减少,还是一样的参数

min-slaves-to-write 1
min-slaves-max-lag 10

如果这样配置,发生脑裂的时候,存在两个 master,master 写的时候,slave 只会给最新的 master 发送 ack,那么超过 10s,旧 master 就拒绝客户端的写请求,那么这个过程最多丢失 10s 的数据

三、哨兵原理

3.1 宕机判断

3.1.1 sdown

主管宕机,如果一个哨兵觉得一个 master 宕机,那么就是主管宕机

判断流程:一个哨兵 ping 一个 master,超过 is-master-down-after-milliseconds 指定毫秒数只会,就主观认为 master 宕机了

3.1.2 odown

客户宕机,如果一个哨兵指定时间内,收到了 quorum 数量的其他哨兵认为 master 是 sdown 了,那么,就认为它是 odown

3.2 哨兵通信

哨兵之间的通信,通过 redis 的 pub/sub 系统实现,每个哨兵都会往 _sentinel_:hello 这个 channel 发送一个消息,这个时候其他哨兵都可以消费这个消息,并感知到其他哨兵的存在

  • 发送:每隔两秒,每个哨兵都会往自己监控的某个 master + slaves 对应的 _sentinel_:hello channel 发送一个消息,内容是 host、ip、runid、master 监控配置

  • 消费:每个哨兵也会去监听自己监控的每个 master + slaves 对应的 _sentinel_:hello channel,从而消费消息,感知同样在监听这个自己也在监听的 master + slaves 的其他哨兵的存在

  • 交换:每个哨兵还会跟其他哨兵交换对 master 的监控配置,互相进行监控配置的同步

3.3 slave 配置纠正

哨兵会负责自动纠正 slave 的一些配置,比如:

slave 如果要成为潜在的 master 候选人,哨兵会确保 slave 复制现有 master 的数据

slave 如果连接到一个错误的 master 上(脑裂),比如故障转移后,那么哨兵会确保它们连接到正确的 master 上

3.4 master 选举算法

如果一个 master 被哨兵确认位 odown,而且 majority 数量的哨兵授权允许主备切换,那么某个哨兵就会执行主备切换操作,此时首先要选举一个 slave,会考虑 slave 的一些信息:

  • 跟 master 断开连接的时长
  • slave 优先级
  • 复制 offset
  • run id

如果一个 slave 跟 master 断开连接的时间已经超过了 down-after-milliseconds 的 10 倍,外加 master 宕机的时长,那么 slave 就被认为不适合选举位 master

time = (down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state

接下来会对 slave 进行排序:

  • 按照优先级:slave 优先级进行排序,slave priority 越低,优先级越高
  • 如果优先级相同,按照 replica offset,哪个 slave 复制越多,offset 越靠后,优先级就越高
  • 如果优先级和 replica offset 都相同,那么选择一个 run id 比较小的那个 slave

3.5 主备切换参数

每次一个哨兵要做主备切换,需要经历两个阶段:

  • 客观宕机 odown 判断:需要 quorum 数量的哨兵认为 odown,然后选举出一个哨兵做切换
  • master 选举授权:这个哨兵还需要得到 majority 数量的哨兵的授权,才能正式执行切换

3.6 configuration epoch

哨兵会对一套 redis master + slaves 进行监控,有相应的监控的配置

执行切换的哨兵,会从要切换到新 master (slave -> master)那里得到一个 configuration epoch,这就是一个 version 号,每次切换的 version 号都必须是唯一的

如果第一次选举出的哨兵切换失败了,那么其他哨兵,会等待 failover-timeout 时间,然后接替继续执行切换,此时会重新获取一个新的 configuration epoch,作为新的 version 号

3.7 configuration 传播

哨兵完成之后,会在本地更新生成最新的 master 配置,然后同步给其他的哨兵,就是通过之前说的 pub/sub 消息机制

一个哨兵完成了一次新的切换后,新的 master 配置是跟着新的 version 号的,其他的哨兵都是根据版本号的大小来更新自己的 master 配置