为什么哨兵机制会存在?
在Redis的主从架构中,主节点负责进行客户端的读操作和写操作,从节点负责客户端的读操作。从节点要保持数据同步,需要从主节点中进行主从复制。那么如果这个时候,主节点挂了,那么首先无法执行客户端的写操作,另一方面,从节点也无法进行数据同步了。
于是,我们首先想到的办法就是人工介入,我们需要从从节点中选择一个新的主节点,然后将从节点的主节点都重新设置为这个新的主节点,然后将客户端配置中的主节点的IP地址更新为新的主节点的IP地址,我们手动操作是非常麻烦的,那么我们在想能否通过一个机制来一直监控主节点,如果主节点挂了,它会自动帮我们完成这些操作。于是,哨兵机制被提出用于解决这个问题。
哨兵是一个运行在特殊的状态下的Redis进程,所以它也是一个节点,它需要完成的任务有三个:监控主节点;如果主节点发生问题,它需要选举出新的主节点;在选举出新的主节点之后,它需要通知其余的从节点和客户端。
如何判断主节点真的故障了?
哨兵会每隔1s向所有的主从节点发送PING命令,并会观察主从节点是否在规定的时间内向自己发送了PING命令的响应。如果没有发送,那么说明其主观下线了。
主节点除了主观下线之外,还存在客观下线。因为主节点没有及时响应消息,不一定是因为主节点故障了,可能是因为其系统压力太大或者网络拥塞所导致的。
为了对主节点的故障判断的更好?于是客观下线被提出,意思就是我们不让一个哨兵来决定主节点是否已经下线,而是存在多个哨兵组成一个哨兵集群。当其中一个哨兵判断主节点主观下线之后,会发送消息给其他的哨兵,其他哨兵会根据自己和主节点之间的情况来投出赞成或者反对票给该哨兵。如果收集到的主观下线的判断大于配置的参数quorum,那么主节点就被判定为客观下线。

由哪个哨兵进行主从故障的转移?
因为一般在设置哨兵的时候都会让哨兵以哨兵集群的方式出现,这样可以减少主节点出现故障的误判概率。那么在让哨兵进行主从故障的转移的时候,我们要从哨兵中获取一个哨兵,让这个哨兵来进行主从故障的转移,这个哨兵被称为Leader,是需要通过选举产生的。
那么具体是如何操作的呢?
哨兵在成为Leader之前,需要成为候选者,成为候选者的哨兵需要判断主节点是客观下线的,也就是其首先需要判断主节点是主观下线的,之后需要收到一定数量的从节点的赞成票,然后赞成主管下线的票数大于配置参数quorum,那么主节点就是客观下线的,那么该哨兵就是候选者哨兵。
那么候选者哨兵如何成为Leader呢?
候选者哨兵需要向其他哨兵获取票数,也就是需要发送命令,表明自己希望成为Leader来执行主从切换。每个候选者哨兵可以投票给自己也可以投票给别人,其余哨兵只可以投票给别人。
当一个候选者满足两个条件:
- 其拿到的票数是哨兵总数的一半以上。
- 其拿到的票数大于等于配置参数quorum。
如果满足这两个条件,那么候选者会成为Leader,后续执行主从节点的切换。
一半来说,在设置哨兵个数的情况下,我们会选择奇数节点的哨兵个数,这样可以防止两个哨兵获取到相同的票数。并且哨兵个数为3或者4,那么它们的容错是相同的,都是1个。那么哨兵个数更少,网络开销也更小。因为一个候选者要同时满足两个条件,后续才能进行主从切换,所以配置参数quorum的值建议设置为哨兵总数的一半+1。
主从故障转移的过程是怎么样的?
主从故障转移主要包含四个阶段:
- 从已下线的主节点中的所有从节点中,选择一个从节点将其转换为新的主节点
- 从其余的从节点修改主从复制的目标,修改为新的主节点
- 将新的主节点的IP地址和信息,通过Redis的订阅者/发布者机制通知给客户端
- 继续监视旧的主节点,当这个旧的主节点重新上线的时候,将其作为新的主节点的从节点。
选出主节点
我们首先想到的选出主节点的方式就是通过随机选择的方式,但是这种方式存在一定的问题,就是我们可能选出网络状况不好的主节点,那么后续可能又要进行主从故障的转移。
所以我们的第一步是将网络状况不好的从节点排除,如果当前的从节点处于主观下线的状态,那么就被排除了。然后,我们判断现存的节点的优先级,优先级的设置是我们人为进行设置的,如果存在节点的内存比较大,那么对于Redis来说比较好,那么优先级设置的就比较高,那么后续我们在选择主节点的时候,选择它也比较好。如果优先级相同,那么就判断哪个从节点的复制进度最快,也就是判断主从复制的过程中,从节点的slave_repl_offset哪个更大,说明其丢失的数据更少,更适合作为主节点;如果复制进度也是一样的,最后我们看哪个从节点的唯一ID小,唯一ID小的从节点作为新的主节点。
后续哨兵Leader向我们选择的新的主节点发送SLAVEOF no one命令,将这个从节点转换为新的主节点。之后,哨兵Leader每隔1s发送INFO命令获取该从节点的消息,知道从节点的角色从slave变为master的时候,哨兵Leader就知道对应的从节点已经升级为了主节点了。
将从节点指向新的主节点
在新的主节点出现之后,哨兵Leader会发送命令SLAVEOF 新的主节点的IP地址 新的主节点的端口号给其余的从节点,从节点接收到了之后执行该命令,对应的从节点就指向了新的主节点。
通知客户端主节点已经更换
通过上述两个阶段哨兵完成了主节点的更换,之后客户端需要知道新的主节点,因为客户端需要主节点来帮自己指向写操作命令。客户端获取新的主节点的方式是通过发布者/订阅者模式订阅哨兵节点的消息。主从切换完成之后,哨兵会发送相应的新的主节点的IP地址和端口号的消息,客户端由于订阅了相应的消息,那么就收到了相应的消息,就可以设置新的主节点的IP地址和端口号了。客户端还可以通过订阅消息来判断主从切换进行到了哪一步。
将旧主节点变为从节点
哨兵集群会继续监督旧的主节点,当旧的主节点上线之后,会向其发送SLAVEOF命令,旧的主节点执行这个命令之后就会成为新的主节点的从节点。
哨兵集群是如何组成的?
在搭建哨兵集群的时候,我们在配置哨兵参数的时候,只需要配置主节点的名称、IP地址和端口号,以及相应的quorum参数,而不需要其余哨兵的任何信息。其原因在于哨兵节点之间是通过发布者/订阅者模式来相互发现的。每个哨兵会将自己的IP地址和端口号发送给主节点,主节点是存在一个频道的,只要哨兵订阅这个频道就可以获取到其余哨兵的IP地址和端口号,从而可以和其余哨兵建立连接。
哨兵集群理论上只知道主节点的信息,它们又是如何知道从节点的信息呢?也是通过主节点来获取的,主节点是知道从节点的信息的。那么哨兵集群通过每隔10s向主节点发送INFO命令来获取所有从节点的信息,主节点会返回给哨兵集群从节点的列表,之后哨兵集群就可以通过从节点的列表来与从节点之间建立连接。