如果客户端发送的都是读操作请求,那还可以由从库继续提供服务,这在纯读的业务场景下还能被接受。但是,一旦有写操作请求了,按照主从库模式下的读写分离要求,需要由主库来完成写操作。此时,也没有实例可以来服务客户端的写操作请求了。

所以,如果主库挂了,我们就需要运行一个新主库,比如说把一个从库切换为主库,把它当成主库。这就涉及到三个问题:

  1. 主库真的挂了吗?
  2. 该选择哪个从库作为主库?
  3. 怎么把新主库的相关信息通知给从库和客户端呢?

在 Redis 主从集群中,哨兵机制是实现主从库自动切换的关键机制,它有效地解决了主从复制模式下故障转移的这三个问题

哨兵其实就是一个运行在特殊模式下的 Redis 进程,主从库实例运行的同时,它也在运行。哨兵主要负责的就是三个任务:监控、选主和通知

redis怎么查看哨兵是否配置成功 redis查看哨兵状态_客户端

 监控:哨兵进程在运行时,周期性地给所有的主从库发送 PING 命令,检测它们是否仍然在线运行。如果从库没有在规定时间内响应哨兵的 PING 命令,哨兵就会把它标记为“下线状态”;同样,如果主库也没有在规定时间内响应哨兵的 PING 命令,哨兵就会判定主库下线,然后开始自动切换主库的流程。

通知相对简单,只需要把新主库信息发给从库和客户端,让它们和新主库建立连接就行,并不涉及决策的逻辑。

但是,在监控和选主这两个任务中,哨兵需要做出两个决策:

  • 在监控任务中,哨兵需要判断主库是否处于下线状态;
  • 在选主任务中,哨兵也要决定选择哪个从库实例作为主库

哨兵判断主库是否处于下线状态?

哨兵对主库的下线判断有“主观下线”和“客观下线”两种。如果检测的是从库,那么,哨兵简单地把它标记为“主观下线”就行了,因为从库的下线影响一般不太大,集群的对外服务不会间断。

但是,如果检测的是主库,那么,哨兵还不能简单地把它标记为“主观下线”,因为很有可能存在这么一个情况:那就是哨兵误判了,其实主库并没有故障。误判一般会发生在集群网络压力较大、网络拥塞,或者是主库本身压力较大的情况下

它通常会采用多实例组成的集群模式进行部署,这也被称为哨兵集群。引入多个哨兵实例一起来判断,就可以避免单个哨兵因为自身网络状况不好,而误判主库下线的情况。同时,多个哨兵的网络同时不稳定的概率较小,由它们一起做决策,误判率也能降低。只有大多数的哨兵实例,都判断主库已经“主观下线”了,主库才会被标记为“客观下线”,这个判断原则就是:少数服从多数。

如何选定新主库?

哨兵选择新主库的过程称为“筛选 + 打分”。简单来说,我们在多个从库中,先按照一定的筛选条件,把不符合条件的从库去掉。然后,我们再按照一定的规则,给剩下的从库逐个打分,将得分最高的从库选为新主库。

redis怎么查看哨兵是否配置成功 redis查看哨兵状态_客户端_02

 在选主时,除了要检查从库的当前在线状态,还要判断它之前的网络连接状态。如果从库总是和主库断连,而且断连次数超出了一定的阈值,我们就有理由相信,这个从库的网络状况并不是太好,就可以把这个从库筛掉了。用配置项 down-after-milliseconds * 10。其中,down-after-milliseconds 是我们认定主从库断连的最大连接超时时间,并且超过10次则说明网络状况不好。

剩余的从库分别是从库优先级、从库复制进度以及从库 ID 号三个规则进行打分。

第一轮:优先级最高的从库得分高。用户可以通过 slave-priority 配置项,比如把内存较大的从库优先级设置大一些。.

第二轮:和旧主库同步程度最接近的从库得分高。最接近主库的的数据比较新,主从库同步时有个命令传播的过程。在这个过程中,主库会用 master_repl_offset 记录当前的最新写操作在 repl_backlog_buffer (环形缓冲区)中的位置,而从库会用 slave_repl_offset 这个值记录当前的复制进度。哪个从库的 slave_repl_offset 最接近 master_repl_offset,那么它的得分就最高,可以作为新主库。master_repl_offset是存储在主库的,但主库已经挂了,怎么获取的这个值?master_repl_offset是单调增加的(slave_repl_offset也是),它的值可以大于repl_backlog_size(缓冲区大小),在实际的选主代码层面,sentinel是直接比较从库的slave_repl_offset,来选择和主库最接近的从库。

第三轮:ID 号小的从库得分高。每个实例都会有一个 ID,在优先级和复制进度都相同的情况下,ID 号最小的从库得分最高,会被选为新主库。

哨兵在操作主从切换的过程中,客户端能否正常地进行请求操作?

如果客户端使用了读写分离,那么读请求可以在从库上正常执行,不会受到影响。但是由于此时主库已经挂了,而且哨兵还没有选出新的主库,所以在这期间写请求会失败

如果不想让业务感知到异常,哨兵需及时通知客户端,让客户端感知主库切换,客户端只能把写失败的请求先缓存起来或写入消息队列中间件中,等哨兵切换完主从后,再把这些写请求发给新的主库。风险是缓存请求过多重放这些请求时间边长。