分布式Redis故障转移(sentinel)

当2台以上Redis实例形成了主备关系,他们组成的集群就具备了一定的高可用:当master 故障时,slave可以成为新的master,对外提供读写服务,这种运行机制称为 failover。那么谁去发现master的故障,并做failover呢?

一种方式是:保持一个daemon进程,监控着所有的master-slave节点,如下所示:

上图中,一个Redis集群里有一个master和2个slave,并且由daemon进程监控着。这种方式存在的问题是:daemon作为单点,无法保证高可用。因此需要引入更多的daemon,如下所示:

上图中,为了解决daemon单点问题,我们引入了2个daemon进程。但是多个daemon 如何对某个master是否可用达成一致?然后如何决策master是否需要failover?

上图中,多个daemon 组成的集群称为 sentinel 集群,每个sentinel节点就是daemon节点。这些节点相互通信、选举、协商,在master节点的故障发现、failover决策上表现出一致性。

sentinel的相互感知

sentinel节点间因为共同监视了同一个master节点,从而也关联了起来。一个新加入的sentinel节点,需要和有相同监视的master的其他sentinel节点相互感知。方式如下:所有需要相互感知的sentinel,都向他们共同的master节点上订阅相同的channel:__sentinel__:hello;新加入的sentinel节点向这个channel发布一条消息,包含了自己的信息,该channel的订阅者们就可以发现这个新的sentinel。随后新的sentinel和已有的其他sentinel建立长连接,如下所示:

上图中,新的sentinel加入后,它向master发送加入信息,然后所有订阅的sentinel节点都会感知到新sentinel节点的存在。

三个定时任务

1、每10秒,每个Sentinel对master和slave执行info。(发现slave节点;确认主从关系)

2、每2秒,每个Sentinel通过master节点的channel交换信息(如上所示:PUB/SUB)。(通过__sentinel__:hello 频道交互;交互对节点的“看法”和自身信息)

3、每1秒,每个Sentinel对其他Sentinel和Redis执行ping。

master的故障发现

sentinel节点通过定期的向master发送心跳包判断其存活状态,称为 PING。一旦发现master没有正常的响应,sentinel将此master置为“主观不可用”。所谓“主观”,意思就是还未得到其他sentinel节点的确认,如下所示:

随后将 “主观不可用” 发送给其他所有的sentinel节点进行确认(is-master-down-by-addr),当确认的 sentinel节点数 >= quorum(可配置)时,则判定该master 为不可用(客观下线),随后进入failover流程。

sentinel monitor <masterName> <ip> <port> <quorum>
如:sentinel monitor myMaster 127.0.0.1 6379 2

sentinel down-after-milliseconds <masterName> <timeout>
如:sentinel down-after-milliseconds myMaster 30000

当master主观不可用时,投票的阀值(quorum)是2;确认“主观不可用”的时间阀值是30S。

sentinel Leader选举

当一台master真正宕机后,可能多个sentinel同时发现了此问题,并通过交互确认“主观不可用”的猜想,然后发起failover。但是最终只能有一个sentinel作为failover的发起者,此时需要开启一个Leader选举的过程,来确定谁来发起failover。

Redis的sentinel机制采用类似 Raft协议 实现这个选举算法:

1、sentinelState的 Epoch 变量,类似于Raft协议中的term(选举回合)。

2、每个确认了master“主观不可用”的sentinel节点,都会向周围广播自己的参选请求。

3、每个接收到参选请求的sentinel节点,如果还没人向它发送过参选请求。它就将本选举回合的意向置位首个参选sentinel并回复;如果已经表示过意向了,则拒绝其他的参选,并将自己的意向回复。

4、每个发送参选请求的sentinel,如果收到了超过一半的同意意向(投票者包含自己),则确定为Leader;如果本回合持续了足够长的时间还未选出Leader,则开启下一个回合。

Leader sentinel确定之后,从所有master的slave中依据一定的规则,选举出一个新的master,并告知其他slave连接这个master。

故障转移failover

1、从slave节点中选出一个“合适的”节点作为新的master节点;

2、对上面的slave节点执行 salveof no one 命令,让其成为master节点;

3、向剩余的slave节点发送命令,让它们成为新的master的slave,复制规则和 parallel-syncs 参数有关;

4、更新对旧的master节点配置为slave,并保持对其关注,当其恢复后命令其去复制新master节点的内容。

slave晋升的规则

1、选择slave-priority (slave节点优先级)最高的节点,如果存在则返回,不存在则继续;

2、选择复制偏移量最大的slave节点(复制最完整的),如果存在则返回,不存在则继续;

3、选择 runId 最小的slave节点(最先启动的)。

总结

Redis Sentinel中的 Sentinel节点 数应该>=3,且最好为奇数;

Redis Sentinel中的 数据节点 与普通节点没有区别;

客户端初始化连接的是Sentinel节点集合,不再是具体的Redis节点,但Sentinel只是配置中心,不是代理

Redis Sentinel 通过3个定时任务,实现了Sentinel 节点对master、slave和其余Sentinel节点的监控;

Redis Sentinel在对节点做失败判定时,分为“主观不可用/主观下线”和“客观不可用/客观下线”;

看懂Redis Sentinel故障转移日志,对于Redis Sentinel以及排查问题非常有帮助;

Redis Sentinel实现读写分离高可用,可以依赖Sentinel节点的消息通知,获取Redis数据节点的状态变化。


参考:《深入分布式缓存》