在主从集群模式下,如果从库发生故障,客户端可以继续向主库或其他从库发送请求,进行相关操作,但是如果主库发生故障,就会直接影响从库同步,如果客户端发送的是读操作,还可以由其他从库继续服务,但是一旦有写操作,就没有实例来服务客户端的写请求操作了。
如果主库挂了,就需要运行一个新主库,比如把一个从库切换为主库。这就涉及到三个问题:
- 主库真的挂了吗?
- 该选择哪个从库作为主库?
- 怎么把新主库的相关信息通知给从库和客户端?
在Redis主从集群中,哨兵机制是实现主从库自动切换的关键机制。哨兵就是一个运行在特殊模式下的Redis进程,它主要负责三个任务:监控、选主和通知。
监控是指哨兵进程在运行时,周期性地给所有的主从库发送ping命令,检测他们是否在线运行。如果从库没有在规定时间响应哨兵的ping命令,哨兵就会把它标记为“下线状态”,如果主库没有在规定时间内响应哨兵的ping命令,哨兵就会判定主库下线,然后开始自动切换主库的流程。
选主是指主库挂了以后,哨兵就需要从多个从库中选择一个从库实例,把它作为新的主库。
通知是指重新选择主库后,哨兵会把新主库的连接信息发送给其他从库,让他们执行replicaof命令,和新主库建立连接,并进行数据复制。同时,哨兵会把新主库的连接信息通知给客户端,让他们把请求操作发送到新主库上。
如何判断主库是否下线?
哨兵对主库的下线判断有“主观下线”和“客观下线”。
哨兵进程会使用ping命令检测它自己和主、从库的网络连接状况,用来判断实例的状态,如果发现主、从库对ping命令的响应超时了,那么哨兵就会把它标记为“主观下线”。
如果检测的是从库,那么,哨兵把它标记为“主观下线”就行了,应为从库的下线影响不大,集群对外服务不会中断。
但是如果检测的是主库,那么哨兵不能简单的把它标记为“主观下线”。因为可能是集群网络压力较大、网络阻塞,或者主库本身压力较大等情况,导致哨兵误判了,一旦启动了主从切换,后续的选主和通知操作会带来额外的计算开销和通信开销。
为了避免这个问题,哨兵机制通常会采用多实例组成的集群模式进行部署,也被称为哨兵集群。引入多个哨兵一起判断,可以避免单个哨兵自身网络状况不好而误判主库下线的情况。同时,多个哨兵的网络同时不稳定的概率小,由他们一起做决策,能有效降低误判率。
在判断主库是否下线时,不能由一个哨兵说了算,只有大多数哨兵实例都判断主库已经“主观下线”时,主库才会被标记为“客观下线”。当有N个哨兵实例时,最好要有N / 2 + 1个实例判断主库为“主观下线”,才能最终判定主库为“客观下线”。具体由多少个实例做出“主观下线”的判断才可以,可以自行设定。
如何选定新主库?
哨兵选择主库的过程为“筛选 + 打分”。
打分规则分别为从库优先级、从库复制进度和从库ID号。
- 第一轮:优先级最高的从库得分高。优先级可以通过slave-priority配置。如果从库优先级一样,哨兵开始第二轮打分;
- 第二轮:和旧主库同步程度最接近的从库得分高。在这个过程中,主库用master_repl_offset记录当前的最新写操作在repl_backlog_buffer中的位置,而从库会用slave_repl_offset记录当前的复制进度。从库的slave_repl_offset越接近master_repl_offset,得分越高,可以作为新主库。如果slave_repl_offset的值一样,就需要进行第三轮打分;
- 第三轮:ID号小的从库得分高。每一个实例都会有一个ID,在优先级和复制进度都相同的情况下,ID号最小的从库得分最高,会被选为新主库。
多个哨兵实例可以降低误判率,但是也会产生一些新的问题:
- 哨兵集群中有实例挂了怎么办?会影响主库状态判断和选择吗?
- 哨兵集群多数实例达成共识,判断出主库“客观下线”后,由哪个实例来执行主从切换呢?