Redis 的主库挂了,如何不间断服务?

2023年 10月 25日 84.0k 0

我们了解到在主从库集群模式下,如果从库发生故障,客户端可以继续向主库或其他从库发送请求,执行相应的操作。然而,当主库发生故障时,会直接影响从库的同步,因为此时从库失去了可用的主库进行数据复制。

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

图片图片

主库故障后,导致从库无法提供写操作的服务,这种情况是不可接受的。因此,在主库发生故障时,我们需要启动一个新的主库,通常是将一个从库升级为主库并将其作为新的主库。然而,这涉及到解决三个核心问题:

  • 如何确定主库已经宕机了?
  • 从众多从库中选择哪一个作为新的主库?
  • 如何通知从库和客户端关于新主库的变化?
  • 这正是哨兵机制的任务。在 Redis 主从集群中,哨兵机制是实现自动主从切换的关键,它成功地解决了上述三个问题,确保系统的可用性。接下来,我们将深入学习和了解哨兵机制的工作原理。

    哨兵机制的基本流程

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

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

    这个流程首先是执行哨兵的第二个任务,选主。主库挂了以后,哨兵就需要从很多个从库里,按照一定的规则选择一个从库实例,把它作为新的主库。这一步完成后,现在的集群里就有了新主库。

    然后,哨兵会执行最后一个任务:通知。在执行通知任务时,哨兵会把新主库的连接信息发给其他从库,让它们执行 replicaof 命令,和新主库建立连接,并进行数据复制。同时,哨兵会把新主库的连接信息通知给客户端,让它们把请求操作发到新主库上。

    我画了一张图片,展示了这三个任务以及它们各自的目标。

    哨兵机制的三项任务与目标哨兵机制的三项任务与目标

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

    在监控任务中,哨兵需要判断主库是否处于下线状态;

    在选主任务中,哨兵也要决定选择哪个从库实例作为主库。

    接下来,我们就先说说如何判断主库的下线状态。

    你首先要知道的是,哨兵对主库的下线判断有“主观下线”和“客观下线”两种。那么,为什么会存在两种判断呢?它们的区别和联系是什么呢?

    主观下线和客观下线

    我先解释下什么是“主观下线”。

    哨兵进程会使用 PING 命令检测它自己和主、从库的网络连接情况,用来判断实例的状态。如果哨兵发现主库或从库对 PING 命令的响应超时了,那么,哨兵就会先把它标记为“主观下线”。

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

    但是,如果检测的是主库,那么,哨兵还不能简单地把它标记为“主观下线”,开启主从切换。因为很有可能存在这么一个情况:那就是哨兵误判了,其实主库并没有故障。可是,一旦启动了主从切换,后续的选主和通知操作都会带来额外的计算和通信开销。

    为了避免这些不必要的开销,我们要特别注意误判的情况。

    首先,我们要知道啥叫误判。很简单,就是主库实际并没有下线,但是哨兵误以为它下线了。误判一般会发生在集群网络压力较大、网络拥塞,或者是主库本身压力较大的情况下。

    一旦哨兵判断主库下线了,就会开始选择新主库,并让从库和新主库进行数据同步,这个过程本身就会有开销,例如,哨兵要花时间选出新主库,从库也需要花时间和新主库同步。而在误判的情况下,主库本身根本就不需要进行切换的,所以这个过程的开销是没有价值的。正因为这样,我们需要判断是否有误判,以及减少误判。

    那怎么减少误判呢?在日常生活中,当我们要对一些重要的事情做判断的时候,经常会和家人或朋友一起商量一下,然后再做决定。

    哨兵机制也是类似的,它通常会采用多实例组成的集群模式进行部署,这也被称为哨兵集群。引入多个哨兵实例一起来判断,就可以避免单个哨兵因为自身网络状况不好,而误判主库下线的情况。同时,多个哨兵的网络同时不稳定的概率较小,由它们一起做决策,误判率也能降低。

    这节课,你只需要先理解哨兵集群在减少误判方面的作用,就行了。至于具体的运行机制,下节课我们再重点学习。

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

    为了方便你理解,我再画一张图展示一下这里的逻辑。

    如下图所示,Redis 主从集群有一个主库、三个从库,还有三个哨兵实例。在图片的左边,哨兵 2 判断主库为“主观下线”,但哨兵 1 和 3 却判定主库是上线状态,此时,主库仍然被判断为处于上线状态。在图片的右边,哨兵 1 和 2 都判断主库为“主观下线”,此时,即使哨兵 3 仍然判断主库为上线状态,主库也被标记为“客观下线”了。

    客观下线的判断客观下线的判断

    简单来说,“客观下线”的标准就是,当有 N 个哨兵实例时,最好要有 N/2 + 1 个实例判断主库为“主观下线”,才能最终判定主库为“客观下线”。这样一来,就可以减少误判的概率,也能避免误判带来的无谓的主从库切换。(当然,有多少个实例做出“主观下线”的判断才可以,可以由 Redis 管理员自行设定)。

    好了,到这里,你可以看到,借助于多个哨兵实例的共同判断机制,我们就可以更准确地判断出主库是否处于下线状态。如果主库的确下线了,哨兵就要开始下一个决策过程了,即从许多从库中,选出一个从库来做新主库。

    如何选定新主库?

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

    新主库的选择过程新主库的选择过程

    在上述段落中,我们需要明晰两个关键的“一定”。现在,让我们详细讨论这里的“一定”究竟指的是什么。

    首先,让我们来探讨所选从库的筛选条件。

    通常情况下,我们必须确保所选的从库仍然处于在线运行状态。然而,在选择新主库时,仅仅考虑从库的当前在线状态是不够的,因为正常在线并不代表它就是最佳的主库选择。

    设想一下,如果在选主时,我们选中一个正常在线的从库并开始使用它。不过,不久后,它的网络连接发生故障,这将迫使我们重新选择主库。这显然不符合我们的期望。

    因此,在进行主库选择时,除了检查从库的当前在线状态,还需要考虑它以前的网络连接状态。如果一个从库经常与主库断开连接,并且断开连接的次数超出了特定的阈值,那么我们就有理由相信,这个从库的网络状况并不太可靠,因此可以将其排除在主库的选择之外。

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

    好了,这样我们就过滤掉了不适合做主库的从库,完成了筛选工作。

    接下来就要给剩余的从库打分了。我们可以分别按照三个规则依次进行三轮打分,这三个规则分别是从库优先级、从库复制进度以及从库 ID 号。只要在某一轮中,有从库得分最高,那么它就是主库了,选主过程到此结束。如果没有出现得分最高的从库,那么就继续进行下一轮。

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

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

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

    这个规则的依据是,如果选择和旧主库同步最接近的那个从库作为主库,那么,这个新主库上就有最新的数据。

    如何判断从库和旧主库间的同步进度呢?

    上节课我向你介绍过,主从库同步时有个命令传播的过程。在这个过程中,主库会用 master_repl_offset 记录当前的最新写操作在 repl_backlog_buffer 中的位置,而从库会用 slave_repl_offset 这个值记录当前的复制进度。

    此时,我们想要找的从库,它的 slave_repl_offset 需要最接近 master_repl_offset。如果在所有从库中,有从库的 slave_repl_offset 最接近 master_repl_offset,那么它的得分就最高,可以作为新主库。

    就像下图所示,旧主库的 master_repl_offset 是 1000,从库 1、2 和 3 的 slave_repl_offset 分别是 950、990 和 900,那么,从库 2 就应该被选为新主库。

    基于复制进度的新主库选主原则基于复制进度的新主库选主原则

    当然,如果有两个从库的 slave_repl_offset 值大小是一样的(例如,从库 1 和从库 2 的 slave_repl_offset 值都是 990),我们就需要给它们进行第三轮打分了。

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

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

    到这里,新主库就被选出来了,“选主”这个过程就完成了。

    我们再回顾下这个流程。首先,哨兵会按照在线状态、网络状态,筛选过滤掉一部分不符合要求的从库,然后,依次按照优先级、复制进度、ID 号大小再对剩余的从库进行打分,只要有得分最高的从库出现,就把它选为新主库。

    小结

    我们已经一起探讨了哨兵机制,这是确保 Redis 提供持续服务的关键要素。具体来说,主从数据库的数据同步是数据可靠性的基石。在主数据库发生故障时,自动执行的主从切换是服务不中断的重要支持。

    Redis 的哨兵机制自动执行以下三项重要功能,实现了主从切换,从而降低了 Redis 集群的维护成本:

  • 监测主库运行状态:监测主库的运行状态,以确定主库是否客观下线,即无法提供有效服务。
  • 选择新主库:一旦主库被客观下线,哨兵机制会选择一个新的主库,以维护集群的可用性。
  • 通知从库和客户端:选定新主库后,哨兵会通知相关从库切换到新的主库,以确保数据同步。同时,客户端也会被重定向到新的主库,以继续访问数据。
  • 为了减少误判,通常使用多个哨兵实例进行部署,它们依据“多数原则”来判断主库的客观下线情况。通常情况下,三个哨兵实例足以支持,只需两个哨兵认定主库已客观下线,切换过程将开始。当需要更高的判断准确性时,可以考虑增加哨兵的数量,例如五个哨兵。

    然而,通过多个哨兵实例来减少误判可能引入新的挑战:

    • 哨兵实例故障处理:哨兵集群中如果有实例发生故障,可能会影响主库状态判断和主从切换。因此,需要有效地管理哨兵集群的高可用性。
    • 主从切换决策:在多数哨兵实例达成共识,认定主库客观下线后,需要决定哪个哨兵实例来执行主从切换。这涉及一些机制,如选举,以确保选出的新主库是最合适的。

    要理解并应对这些挑战,需要深入了解哨兵集群及其配置。这有助于确保 Redis 集群在面临故障时仍然能够提供稳定的高可用性服务。在下一篇中,我们将深入探讨哨兵集群的工作原理和相关问题。

    相关文章

    Oracle如何使用授予和撤销权限的语法和示例
    Awesome Project: 探索 MatrixOrigin 云原生分布式数据库
    下载丨66页PDF,云和恩墨技术通讯(2024年7月刊)
    社区版oceanbase安装
    Oracle 导出CSV工具-sqluldr2
    ETL数据集成丨快速将MySQL数据迁移至Doris数据库

    发布评论