美文网首页
Redis哨兵模式高可用原理

Redis哨兵模式高可用原理

作者: 匠丶 | 来源:发表于2021-11-29 07:40 被阅读0次

    我们知道主从复制是高可用的基石,从库宕机依然可以将请求发送给主库或者其他从库,但是 Master 宕机,只能响应读操作,写请求无法再执行。

    所以主从复制架构面临一个严峻问题,主库挂了,无法执行「写操作」,无法自动选择一个 Slave 切换为 Master,也就是无法故障自动切换。

    什么是哨兵(Sentinel)

    搭建实例采用三个哨兵形成集群,三个数据节点(一主两从)方式搭建,如下图所示:

    哨兵机制的主要任务

    哨兵是 Redis 的一种运行模式,它专注于对 Redis 实例(主节点、从节点)运行状态的监控,并能够在主节点发生故障时通过一系列的机制实现选主及主从切换,实现故障转移,确保整个 Redis 系统的可用性。结合 Redis 的 官方文档:https://redis.io/topics/sentinel,可以知道 Redis 哨兵具备的能力有如下几个:

    • 监控:持续监控 master 、slave 是否处于预期工作状态。
    • 自动切换主库:当 Master 运行故障,哨兵启动自动故障恢复流程:从 slave 中选择一台作为新 master。
    • 通知:让 slave 执行 replicaof ,与新的 master 同步;并且通知客户端与新 master 建立连接。

    监控

    在默认情况下,Sentinel 通过以每秒一次的频率向所有包括 Master、Slave、其他 Sentinel 在内)发送 PING 命令,如果 slave 没有在在规定时间内响应「哨兵」的 PING 命令,「哨兵」就认为这哥们可能嗝屁了,就会将他记录为「下线状态」;

    假如 master 没有在规定时间响应 「哨兵」的 PING 命令,哨兵就判定master下线,开始执行「自动切换 master 」的流程。

    PING 命令的回复有两种情况:

    有效回复:返回 +PONG、-LOADING、-MASTERDOWN 任何一种;
    无效回复:有效回复之外的回复,或者指定时间内返回任何回复。

    为了防止master「假死」,「哨兵」设计了「主观下线」和「客观下线」两种暗号。

    主观下线
    哨兵利用 PING 命令来检测master、 slave 的生命状态。如果是无效回复,哨兵就把这个哥们标记为「主观下线」。

    因为有可能出现误判,master并没有嗝屁,一旦启动了master切换,后续的选主、通知,slave 花时间与新 master 同步数据都会消耗大量资源。

    所以「哨兵」要降低误判的概率,误判一般会发生在集群网络压力较大、网络拥塞,或者是主库本身压力较大的情况下。

    既然一个人容易误判,那就多个人一起投票判断。哨兵机制也是类似的,采用多实例组成的集群模式进行部署,这就是哨兵集群。引入多个哨兵实例一起来判断,就可以避免单个哨兵因为自身网络状况不好,而误判主库下线的情况。

    同时,多个哨兵的网络同时不稳定的概率较小,由它们一起做决策,误判率也能降低。

    客观下线
    判断 master 是否下线不能只有一个「哨兵」说了算,只有过半的哨兵判断 master 已经「主观下线」,这时候才能将 master 标记为「客观下线」,也就是说这是一个客观事实。

    主观下线与客观下线的区别
    简单来说,主观下线是哨兵自己认为节点宕机,而客观下线是不但哨兵自己认为节点宕机,而且该哨兵与其他哨兵沟通后,达到一定数量的哨兵都认为该哥们嗝屁了。

    这里的「一定数量」是一个法定数量(Quorum),是由哨兵监控配置决定的,解释一下该配置:



    这条配置项用于告知哨兵需要监听的主节点:

    • sentinel monitor:代表监控。
    • mymaster:代表主节点的名称,可以自定义。
    • 192.168.11.128:代表监控的主节点 ip,6379 代表端口。
    • 2:法定数量,代表只有两个或两个以上的哨兵认为主节点不可用的时候,才会把 master 设置为客观下线状态,然后进行 failover 操作。

    「客观下线」的标准就是,当有 N 个哨兵实例时,要有 N/2 + 1 个实例判断 master 为「主观下线」,才能最终判定 Master 为「客观下线」,其实就是过半机制。

    自动切换主库

    「哨兵」的第二个任务,选择新 master 。按照一定的 「筛选条件」 + 「打分」 策略,将得分最高者选为新 master。


    筛选条件
    • 从库当前在线状态,下线的直接丢弃;
    • 评估之前的网络连接状态 down-after-milliseconds * 10:如果从库总是和主库断连,而且断连次数超出了一定的阈值(10 次),我们就有理由相信,这个从库的网络状况并不是太好,就可以把这个从库筛掉了。

    打分
    过滤掉不合适的 slave 之后,则进入打分环节。打分会按照三个规则进行三轮打分,规则分别为:
    1、slave 优先级,通过 slave-priority 配置项,给不同的从库设置不同优先级(后台有人没办法),优先级高的直接晋级为新 master 。
    2、slave_repl_offset与 master_repl_offset进度差距,如果都一样,那就继续下一个规则。其实就是比较 slave 与旧 master 复制进度的差距;
    3、slave runID,在优先级和复制进度都相同的情况下,ID 号最小的从库得分最高,会被选为新主库。(论资排辈,根据 runID 的创建时间来判断,时间早的上位);

    通知

    哨兵集群工作原理

    「哨兵」并不是一个人,多个人共同组成一个「哨兵集群」,即使有一些「哨兵」被老王打死了,其他的「哨兵」依然可以共同协作完成监控、选举以及通知 slave 、master 以及客户端。

    在配置哨兵集群的时候,哨兵配置中只设置了监控的 master IP 和 port,并没有配置其他哨兵的连接信息。

    哨兵之间是如何知道彼此的?如何知道 slave 并监控他们的?由哪一个「哨兵」执行主从切换呢?

    pub/sub 实现哨兵间通信和发现 slave

    哨兵之间可以相互通信搞事情,主要归功于 Redis 的 pub/sub 发布/订阅机制。

    哨兵与 master 建立通信,利用 master 提供发布/订阅机制发布自己的信息,比如身高体重、是否单身、IP、端口……

    master 有一个 sentinel:hello 的专用通道,用于哨兵之间发布和订阅消息。这就好比是 sentinel:hello 微信群,哨兵利用 master 建立的微信群发布自己的消息,同时关注其他哨兵发布的消息。

    当多个哨兵实例都在主库上做了发布和订阅操作后,它们之间就能知道彼此的 IP 地址和端口,从而相互发现建立连接。

    Redis 通过频道的方式对消息进行分别管理,这里的频道其实就是不同的微信群。

    哨兵之间虽然建立连接了,但是还需要和 slave 建立连接,不然没法监控他们呀,如何知道 slave 并监控他们的?

    关键还是利用 master 来实现,哨兵向 master 发送 INFO 命令, master 自然是知道所有的 salve的。所以 master 接收到命令后,便将 slave 列表告诉哨兵。

    哨兵根据 master 响应的 slave 名单信息与每一个 salve 建立连接,并且根据这个连接持续监控哨兵。

    选择哨兵执行主从切换

    这个跟哨兵判断 master “客观下线”类似,也是通过投票的方式选出来的。

    任何一个哨兵判断 master “主观下线”后,就会给其他哨兵基友发送 is-master-down-by-addr 命令,好基友则根据自己跟 master 之间的连接状况分别响应 Y 或者 N ,Y 表示赞成票, N 就是反对。

    如果某个哨兵获得了大多数哨兵的“赞成票”之后,就可以标记 master 为 “客观下线”,赞成票数是通过哨兵配置文件中的 quorum 配置项设定。

    获得多数赞成票的哨兵可以向其他哨兵发送命令,申明自己想要执行主从切换。并让其他哨兵进行投票,投票过程就叫做 “Leader 选举”。

    想要成为 “Leader”没那么简单,得有两把刷子。需要满足以下条件:

    1、获得其他哨兵基友过半的赞成票;
    2、赞成票的数量还要大于等于配置文件的 quorum 的值。

    通过 pub/sub 实现客户端事件通知

    新 master 选出来了,要怎么公示天下呢?

    在 Redis 也是类似,通过 pub/sub 机制发布不同事件,让客户端在这里订阅消息。客户端可以订阅哨兵的消息,哨兵提供的消息订阅频道有很多,不同频道包含了主从库切换过程中的不同关键事件。

    也就是在不同的“微信群”发布不同的事件,让对该事件感兴趣的人进群即可。

    相关文章

      网友评论

          本文标题:Redis哨兵模式高可用原理

          本文链接:https://www.haomeiwen.com/subject/fmchxrtx.html