Redis - 高可用架构
一、哨兵机制
1.1 功能
哨兵是 Redis 集群机构中非常重要的一个组件,主要有以下功能:
- 集群监控:负责监控 Master 和 Slave 进程是否正常工作
- 消息通知:如果某个 Redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员
- 故障转移:如果 Master 挂了,会自动转移到 Salve 上
- 配置中心:如果发生主备切换,通知 Client 客户端新的 Master 地址
1.2 作用
哨兵用于实现 Redis 集群的高可用,本身也是分布式的,作为一个哨兵集群去运行,互相协同工作
主备切换时,判断一个 Master 是否宕机,需要大部分哨兵都同意,达成 master 是客观宕机
才行,接着还需要哨兵进行授权,只有大多数哨兵进行授权了,才能正式进行主备切换
这里哨兵节点挂了,哨兵集群要保证能正常工作,因为如果作为一个高可用架构的重要组成部分,它是单点的,那就根本无法保证架构高可用了
二、哨兵切换
2.1 部署注意点
- 哨兵至少需要 3 个实例,来保证自己的健壮性
- 哨兵 + Redis 主从的部署架构,无法保证数据零丢失,只能保证 Redis 集群的高可用
- 对于哨兵 + Redis 主从这种复杂的部署架构,尽量在测试环境和生产环境都进行充足的测试和演练
2.2 主备切换
2.2.1 过程
在 Redis 里面,主备切换这个过程有两个:
- 故障判断:需要一定数量的哨兵进行选举,判断故障(客观宕机)
- 授权切换:需要一定数量的哨兵进行授权,从而进行主备切换
哨兵的故障判断,通过 quorum
参数来调整:
通过设置 quorum
这个参数,这个参数的意义理解为 “法定人数”,也就是说,只有哨兵集群存在大于或等于 quorum
数量的哨兵都认为 master 挂掉了,才会做一个选举,选举出一个哨兵去做故障转移
选举出了一个哨兵后,就要进行主备切换了,而进行主备切换,还需要进行授权
哨兵的授权切换,通过 majority
参数来调整:
通过设置 majority
这个参数,这个参数的意义理解为 “投票数”,也就是说,只有哨兵集群存在大于或等于 majority
数量的哨兵认同可以授权,才能正式进行主从切换
从中可以看出,进行一次主备切换:
- 如果
quorum < majority
,那么大于或等于majority
的哨兵授权即可执行主备切换 - 如果
quorum >= majority
,那么大于或等于quorum
的哨兵都授权才能进行主备切换
2.2.2 举例
2 节点哨兵集群:
四个进程如下,每个节点对应一个 Redis 普通进程和哨兵进程,Redis 进程为 Master
和 Redis-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 的 - 如果
M1
和S1
所在的主机宕机了,那么哨兵只剩一个,此时就没有majority
的数量来运行执行故障转移了
经典的 3 节点哨兵集群:
六个进程如下,每个节点对应一个 Redis 普通进程和哨兵进程,Redis 进程为 Master
和 Redis-Slave
+----+
| M1 |
| S1 |
+----+
|
+----+ | +----+
| R2 |----+----| R3 |
| S2 | | S3 |
+----+ +----+
- 配置
quorum = 2
,master 所在节点宕机,也就是M1
的那个节点,那么三个哨兵剩下两个 S2
与S3
可以一致认为 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 配置