1)概念

主从机制可以缓解主库的压力,并且从库挂了也不影响业务;但是如果主库挂壁了,那么写请求无法接受,业务操作就瘫痪了。对于这个问题,就需要有机制来保障主库挂掉之后可以有其他的库充当主库,接棒任务,这时候需要解决三个基本问题:怎么判断主库挂壁了?主库挂了谁顶上来?顶上来的库的信息怎么让客户端知道?
哨兵可以解决主从模式下的故障转移问题,对应上面三个问题,可见哨兵的基本功能就是监控、选主、通知。首先哨兵可以发现主库宕机的情况,接着在从库中选择一个顶上来充当主库,最后将主库的信息通知到其他的从库与客户端,这就完成了一次故障转移,保证了系统的高可用性。接下来详细的描述一下各个过程的细节。

2)监控——怎么判断主库挂壁了

哨兵是一种特殊模式下的Redis服务器,因此它要实现对主从库的监控,首先就要建立连接。sentinel会与主库创建命令连接和订阅连接,命令连接主要是为了发送诸如探测这些命令的,而订阅连接是用来接收信息的。与主库建立完连接之后,sentinel默认每10秒会向主库发送一条INFO命令,可以获取到库id以及从库信息,接着根据这些从库信息去与从库建立连接,同样是每10秒发送一条INFO命令。
先看看哨兵集群是怎么搭建起来的。得益于Redis的Pub/Sub机制,多台哨兵连接到同一台主库上的时候,都会订阅 sentinel :hello 频道,并且每台哨兵每2秒会向频道发送自己的信息,并且接受频道传送的其他哨兵的信息,以此达到哨兵集群的搭建,无须手动指定,自动发现。
哨兵又是怎么判定主库下线的呢? 这里分为主观下线和客观下线两种类型。在连接过程中sentinel每1秒会向主从库发送PING命令维护连接,如果发现从库未有响应那么这时会将其标为主观下线,无需其他操作。如果发现主库未有响应,这时需要注意误判的情况,也就是网络阻塞带来的命令回复延时问题,这时候需要引入哨兵集群来解决,也就是只有quorum台哨兵都判定主库主观下线的情况下,才能说主库已经失联了,这时候就进入了客观下线的情况,也就是可以进行主从切换的时机了。

3)选主库——主库挂了谁顶上来

首先了解 down-after-milliseconds 配置项,其表示主从库断连的最大连接超时时间,我们在选择从库的时候需要根据网络情况来选择,如果主从库发生断连次数到10次以上,也就是 down-after-milliseconds * 10,那么我们就可以判定这个从库是不稳定的,我们选举主库的时候不考虑它。
完成筛选之后我们就要选择从库了,一般按照三个规则:从库优先级高的先入选与旧主库同步程度高(master_repl_offset 与 slave_repl_offset的差值)的先入选ID小的先入选。这三个规则是按照顺序的,如果优先级一样,则比对同步程度,如果同步程度一样则比较ID。到这里从库的选举就完成了,新主库诞生了。

4)通知——顶上来的库的信息怎么让客户端知道

这也可以基于Pub/Sub机制来完成,sentinel本质上也是Redis实例,因此其也提供了订阅发布机制,我们可以让客户端订阅哨兵的指定频道,当特定事件比如换主时,客户端就可以从频道的订阅消息上收到新主的信息,从而完成信息发送。具体的频道见示意图:
20、哨兵/哨兵集群 - 图1

5)哨兵选举——怎么确定让哪一台机器执行主从切换

这其实也涉及到投票选举的过程。当哨兵判定主库进入主观下线状态,就会向其他哨兵发送 is-master-down-by-addr 命令,其他哨兵根据情况回复Y或者N,当哨兵得到的Y回复数为quorum的时候(包括自己的一票),就会将主库标记为客观下线,并且向其他哨兵发送命令表明自己想执行主从切换,当哨兵得到的票数大于等于(N/2+1) 同时大于等于quorum值,就成为了哨兵leader,可以开始执行主从切换。
所有哨兵都有被选举的资格,也有选举他人的资格(每个哨兵只有一票),他们在判定主库客观下线后会将这一票投给自己,并且向其他哨兵发送选举投票请求,如果其他哨兵当前还没进入判定客观下线的流程,那么他的票会投给第一个向他发请求的哨兵,如果他进入了客观下线的流程那么自然就会拒绝这些投票请求。
当哨兵得到的票数过半并且不少于quorum的时候,他就在这一轮中成为了哨兵leader,可以执行主从切换了。如果这个过程中发生了平票或者未产生leader的情况,那么哨兵集群会等待一段时间(哨兵故障转移超时时间的 2 倍)之后再进行投票拉票,每台哨兵都会给这个时间加上一个随机数,以防止所有哨兵同时竞争,这样重试下去总会选举出来leader。
哨兵leader在主从切换的时候会每1秒向被升级的从库发送INFO命令(平时是每10秒),观察是否成功变更为master。