哨兵机制:主库挂了,Redis如何不间断服务
问题聚焦:
- 主从库集群模式下,如果主库发生故障,从库没有相应的主库可以进行数据复制操作,如何保证服务不间断?
- 如果主库发生故障,读操作可以由从库继续提供服务,但是写操作如何处理?
无论是写服务中断,还是从库无法进行数据同步,都是不能接受的。所以,如果主库挂了,我们就需要运行一个新主库,比如说把一个从库切换为主库,把它当成主库。这就涉及到三个问题:
- 主库真的挂了吗?
- 该选择哪个从库作为主库?
- 怎么把新主库的相关信息通知给从库和客户端呢?
这里我们就要用到Redis的哨兵机制,在Redis主从集群中,哨兵机制是实现主从库自动切换的关键机制,可以有效地解决主从复制模式下故障转移的这三个问题。
哨兵机制的基本流程
哨兵作为一个运行在特殊模式下的Redis进程,主要负责的任务为:监控、选主和通知。
监控
- 监控是指哨兵进程在运行时,周期性地给所有的主从库发送PING命令,检测它们是否仍然在线运行。如果从库没有在规定时间内响应哨兵的PING命令,哨兵就会把它标记为“下线状态”;同样,如果主库也没有在规定时间内响应哨兵的PING命令,哨兵就会判定主库下线,然后开始自动切换主库 的流程。
选主
- 主库挂了以后,哨兵就需要从很多个从库,按照一定的规则选择一个从库实例,把它作为新的主库。这一步完成后,现在的集群里就有了新主库
通知
- 在执行通知任务时,哨兵会把新主库的连接信息发给其他从库,让它们执行replicaof命令,和新主库建立连接,并进行数据复制。同时,哨兵会把新主库的连接信息通知给客户端,让它们把请求操作发到新主库上。
在这三个任务中,通知任务相对来说比较简单,哨兵只需要把新主库信息发给从库和客户端,让它们和新主库建立连接,并不涉及决策的逻辑。
但是在监控和选主这两个任务中,哨兵需要做出两个决策:
- 在监控任务中,哨兵需要判断主库是否处于下线状态
- 在选主任务中,哨兵也要决定选择哪个从库实例作为主库
首先我们先分析如何判断主库的下线状态
判断主观下线和客观下线
哨兵对主库的下线判断有“主观下线”和“客观下线”两种。
- 哨兵进程会使用
PING
命令检测它自己和主、从库的网络连接情况,用来判断实例的状态。 - 如果哨兵发现主 库或从库对PING命令的响应超时了,那么,哨兵就会先把它标记为“主观下线”
- 如果检测的是从库,哨兵简单地把它标记为“主观下线”就行了,因为从库的下线影响一般不太大, 集群的对外服务不会间断
但是如果检测的是主库,那么,哨兵还不能简单地把它标记为“主观下线”,并开启主从切换。因为很有可能存在这么一个情况:哨兵误判,即主库并没有故障(在集群网络压力较大、网络拥塞,或者是主库本身压力较大的情况下)。一旦启动了主从切换,后续的选主和通知操作都会带来额外的计算和通信开销。
为了避免这些不必要的开销,我们要特别注意误判的情况
哨兵机制通常会采用多实例组成的集群模式进行部署,即哨兵集群。引入多个哨兵实例一起来判断,就可以避免单个哨兵因为自身网络状况不好,而误判主库下线的情况。同时多个哨兵的网络同时不稳定的概率较小,由它们一起做决策,也能降低误判率。
- 在判断主库是否下线时,不能由一个哨兵说了算,只有大多数的哨兵实例都判断主库已经“主观下线”了,主库才会被标记为“客观下线”,表明主库下线成为一个客观事实了。这个判断原则就是:少数服从多数。同时,这会进一步触发哨兵开始主从切换流程
简单来说,“客观下线”的标准就是,当有N个哨兵实例时,最好要有N/2 + 1个实例判断主库为“主观下线”,才能最终判定主库为“客观下线”。这样一来,就可以减少误判的概率,也能避免误判带来的无谓的主从库切换。
如何选定新主库?
- 哨兵选择新主库的过程可称为“筛选+打分”。我们在多个从库中,先按照一定的筛选条件,把不符合条件的从库去掉。然后再按照一定的规则,给剩下的从库逐个打分,将得分最高的从库选为新主库
更具体的我们来看一下选新主库的筛选条件和打分规则。
选主的筛选条件
- 一般情况下,我们肯定要先保证所选的从库仍然在线运行。另外除了要检查从库的当前在线状态,还要判断它之前的网络连接状态。(减少该库很快出现故障的几率)
- 如果从库总是和主库断连,而且断连次数超出了一定的阈值,我们就有理由相信,这个从库的网络状况并不是太好,就可以把这个从库筛掉了
- 具体的判断可以使用配置项down-after-milliseconds * 10。其中,down-after-milliseconds是我们认 定主从库断连的最大连接超时时间。如果在down-after-milliseconds毫秒内,主从节点都没有通过网络联系上,我们就可以认为主从节点断连了。如果发生断连的次数超过了10次,就说明这个从库的网络状况不好,不适合作为新主库。
完成了筛选后就要给剩余的从库打分。我们可以分别按照三个规则依次进行三轮打分,这三个规则分别是从库优先级、从库复制进度以及从库ID号。只要在某一轮中,有从库得分最高,那么它就被选为主库了,选主过程到此结束。如果没有出现得分最高的从库,那么就继续进行下一轮。
- 1.优先级最高的从库得分高
可以通过slave-priority配置项,给不同的从库设置不同优先级。比如有两个从库,可以手动给内存大的实例设置一个高优先级。在选主时,哨兵会给优先级高的从库打分,如果有一个从库优先级最高,那么它就是新主库了。 - 2.和旧主库同步程度最接近的从库得分高
主从库同步时有个命令传播的过程。在这个过程中,主库会用master_repl_offset
记 录当前的最新写操作在repl_backlog_buffer
中的位置,而从库会用slave_repl_offset
这个值记录当前的复制进度。
此时,我们想要找的从库,它的slave_repl_offset需要最接近master_repl_offset。如果在所有从库中,有 从库的slave_repl_offset最接近master_repl_offset,那么它的得分就最高,可以作为新主库。 - 3.ID号小的从库得分高
每个实例都会有一个ID,这个ID就类似于这里的从库的编号。Redis在选主库时,有一个默认的规定:在优先级和复制进度都相同的情况下,ID号最小的从库得分最高,会被选为新主库。
自此新主库就被选出来了。
小结
为了实现主从库的自动切换,Redis的哨兵机制自动完成了以下三大功能:
- 监控主库运行状态,并判断主库是否客观下线
- 在主库客观下线后,选取新主库
- 选出新主库后, 通知从库和客户端