JUC

哨兵模式解决什么问题


哨兵模式解决什么问题

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

哨兵机制的基本流程

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

在这三个任务中,通知任务相对来说比较简单,哨兵只需要把新主库信息发给从库和客户端,让它们和新主库建立连接就行,并不涉及决策的逻辑。

监控

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

主观下线和客观下线

如果哨兵发现主库或从库对 PING 命令的响应超时了,那么,哨兵就会先把它标记为“主观下线”。

为了==防止误判常会采用多实例组成的集群模式进行部署==,这也被称为哨兵集群。引入多个哨兵实例一起来判断,就可以避免单个哨兵因为自身网络状况不好,而误判主库下线的情况。

在判断主库是否下线时,不能由一个哨兵说了算,只有大多数的哨兵实例,都判断主库已经“主观下线”了,主库才会被标记为“==客观下线==”,这个叫法也是表明主库下线成为一个客观事实了。这个判断原则就是:少数服从多数。同时,这会进一步触发哨兵开始主从切换流程。

选主

我们在多个从库中,先按照==一定的筛选条件==,把不符合条件的从库去掉。然后,我们再按照==一定的规则==,给剩下的从库逐个打分,将得分最高的从库选为新主库,如下图所示:

筛选的条件

除了要检查从库的当前在线状态,还要判断它之前的网络连接状态。

具体怎么判断呢?你使用配置项 ==down-after-milliseconds * 10==。其中,down-after-milliseconds 是我们认定主从库断连的最大连接超时时间。如果在 down-after-milliseconds 毫秒内,主从节点都没有通过网络联系上,我们就可以认为主从节点断连了。如果发生断连的次数超过了 10 次,就说明这个从库的网络状况不好,不适合作为新主库。

打分

第一轮:优先级最高的从库得分高。

用户可以通过 ==slave-priority 配置项,给不同的从库设置不同优先级==。比如,你有两个从库,它们的内存大小不一样,你可以手动给内存大的实例设置一个高优先级。在选主时,哨兵会给优先级高的从库打高分,如果有一个从库优先级最高,那么它就是新主库了。如果从库的优先级都一样,那么哨兵开始第二轮打分。

第二轮:和旧主库同步程度最接近的从库得分高。

主从库同步时有个命令传播的过程。在这个过程中,主库会用 master_repl_offset 记录当前的最新写操作在 repl_backlog_buffer 中的位置,而从库会用 slave_repl_offset 这个值记录当前的复制进度。 此时,我们想要找的从库,它的 ==slave_repl_offset 需要最接近 master_repl_offset==。如果在所有从库中,有从库的 slave_repl_offset 最接近 master_repl_offset,那么它的得分就最高,可以作为新主库。

第三轮:ID 号小的从库得分高。

每个实例都会有一个 ID,这个 ID 就类似于这里的从库的编号。目前,Redis 在选主库时,有一个默认的规定:==在优先级和复制进度都相同的情况下,ID 号最小的从库得分最高==,会被选为新主库。

小结

Redis 的哨兵机制自动完成了以下三大功能,从而实现了主从库的自动切换,可以降低 Redis 集群的运维开销: 1. 监控主库运行状态,并判断主库是否客观下线; 2. 在主库客观下线后,选取新主库; 3. 选出新主库后,通知从库和客户端。

为了降低误判率,在实际应用时,哨兵机制通常采用多实例的方式进行部署,==多个哨兵实例通过“少数服从多数”的原则==,来判断主库是否客观下线。一般来说,我们可以部署三个哨兵,如果有两个哨兵认定主库“主观下线”,就可以开始切换过程。


基于 pub/sub 机制的哨兵集群组成

在配置哨兵的信息时,我们只需要用到下面的这个配置项,设置主库的 IP 和端口,并没有配置其他哨兵的连接信息。

sentinel monitor <master-name> <ip> <redis-port> <quorum> 

哨兵实例之间可以相互发现,要归功于 Redis 提供的 pub/sub 机制,也就是发布 / 订阅机制。

哨兵只要和主库建立起了连接,就可以在主库上发布消息了,比如说发布它自己的连接信息(IP 和端口)。同时,它也可以从主库上订阅消息,获得其他哨兵发布的连接信息。当多个哨兵实例都在主库上做了发布和订阅操作后,它们之间就能知道彼此的 IP 地址和端口。

除了哨兵实例,我们自己编写的应用程序也可以通过 Redis 进行消息的发布和订阅。所以,为了区分不同应用的消息,Redis 会以频道的形式,对这些消息进行分门别类的管理。所谓的频道,实际上就是消息的类别。当消息类别相同时,它们就属于同一个频道。反之,就属于不同的频道。==只有订阅了同一个频道的应用,才能通过发布的消息进行信息交换。==

在主从集群中,主库上有一个名为“sentinel:hello”的频道,不同哨兵就是通过它来相互发现,实现互相通信的。

我来举个例子,具体说明一下。在下图中,哨兵 1 把自己的 IP(172.16.19.3)和端口(26579)发布到“sentinel:hello”频道上,哨兵 2 和 3 订阅了该频道。那么此时,哨兵 2 和 3 就可以从这个频道直接获取哨兵 1 的 IP 地址和端口号。 然后,哨兵 2、3 可以和哨兵 1 建立网络连接。通过这个方式,哨兵 2 和 3 也可以建立网络连接,这样一来,哨兵集群就形成了。它们相互间可以通过网络连接进行通信,比如说对主库有没有下线这件事儿进行判断和协商。

==哨兵除了彼此之间建立起连接形成集群外,还需要和从库建立连接==。这是因为,在哨兵的监控任务中,它需要对主从库都进行心跳判断,而且在主从库切换完成后,它还需要通知从库,让它们和新主库进行同步。

哨兵是如何知道从库的 IP 地址和端口的呢?

这是由哨兵向主库发送 ==INFO 命令==来完成的。就像下图所示,哨兵 2 给主库发送 INFO 命令,主库接受到这个命令后,就会把从库列表返回给哨兵。接着,哨兵就可以根据从库列表中的连接信息,和每个从库建立连接,并在这个连接上持续地对从库进行监控。哨兵 1 和 3 可以通过相同的方法和从库建立连接。

你看,==通过 pub/sub 机制,哨兵之间可以组成集群==,同时,哨兵又==通过 INFO 命令,获得了从库连接信息==,也能和从库建立连接,并进行监控了。

在实际使用哨兵时,我们有时会遇到这样的问题:==如何在客户端通过监控了解哨兵进行主从切换的过程呢==?比如说,主从切换进行到哪一步了?这其实就是要求,客户端能够获取到哨兵集群在监控、选主、切换这个过程中发生的各种事件。

我们仍然可以依赖 pub/sub 机制,来帮助我们完成哨兵和客户端间的信息同步

从本质上说,哨兵就是一个运行在特定模式下的 Redis 实例,只不过它并不服务请求操作,只是完成监控、选主和通知的任务。所以,每个哨兵实例也提供 pub/sub 机制,客户端可以从哨兵订阅消息。哨兵提供的消息订阅频道有很多,不同频道包含了主从库切换过程中的不同关键事件。

由哪个哨兵执行主从切换?

==任何一个实例只要自身判断主库“主观下线”后==,就会给其他实例发送 is-master-down-by-addr 命令。接着,其他实例会根据自己和主库的连接情况,做出 Y 或 N 的响应,Y 相当于赞成票,N 相当于反对票。

一个哨兵获得了仲裁所需的赞成票数后,就可以标记主库为“客观下线”。这个所需的赞成票数是通过哨兵配置文件中的 quorum 配置项设定的。

例如,现在有 5 个哨兵,quorum 配置的是 3,那么,一个哨兵需要 3 张赞成票,就可以标记主库为“客观下线”了。这 3 张赞成票包括哨兵自己的一张赞成票和另外两个哨兵的赞成票。此时,==这个哨兵就可以再给其他哨兵发送命令,表明希望由自己来执行主从切换,并让所有其他哨兵进行投票。这个投票过程称为“Leader 选举”==。因为最终执行主从切换的哨兵称为 Leader,投票过程就是确定 Leader。

如果哨兵集群只有 2 个实例,此时,一个哨兵要想成为 Leader,必须获得 2 票,而不是 1 票。所以,如果有个哨兵挂掉了,那么,此时的集群是无法进行主从库切换的。因此,==通常我们至少会配置 3 个哨兵实例==。

小结

  1. 基于 pub/sub 机制的哨兵集群组成过程;
  2. 基于 INFO 命令的从库列表,这可以帮助哨兵和从库建立连接;
  3. 基于哨兵自身的 pub/sub 功能,这实现了客户端和哨兵之间的事件通知。

最后,我想再给你分享一个经验:==要保证所有哨兵实例的配置是一致的,尤其是主观下线的判断值 down-after-milliseconds==。我们曾经就踩过一个“坑”。当时,在我们的项目中,因为这个值在不同的哨兵实例上配置不一致,导致哨兵集群一直没有对有故障的主库形成共识,也就没有及时切换主库,最终的结果就是集群服务不稳定。所以,你一定不要忽略这条看似简单的经验。

  • 作者:低调做个路人 (扫码联系作者)
  • 发表时间:2022-11-02 14:52:52
  • 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
  • 评论

    A
    你好
    66
    haha
    999
    aha
      @999
    ( '▿ ' )
    哇哈哈测试
    555
    哇哈哈测试
    aaaa
    哇哈哈测试
    as
    哇哈哈测试
    888
    哇哈哈测试
    777
    哇哈哈测试
    6666
    lurenpeng   @哇哈哈测试
    你好
    lurenpeng   @peng
    who are you
    xxx   @peng
    你好
    peng   @哇哈哈测试
    评论下你