Redis核心技术与实战【学习笔记】 - 3.Redis服务高可靠

news2024/12/27 13:37:38

1.数据同步:主从库如何实现数据一致?

前面我们学习了 AOF 和 RDB,如果 Redis 发生了宕机,它们可以分别通过回放日志和重新读入 RDB 文件的方式恢复数据,从而保证尽量较少丢失数据,提升可靠性。

不过,即使使用了这两种方法,也依然存在服务不可以的问题。比如说,只运行一个 Redis 实例,如果这个实例宕机了,它在恢复期间,是无法服务新来的数据操作请求的。

我们常说的 Redis 具有高可靠其实有两层含义,一是数据尽量减少丢失,而是服务尽量减少中断。AOF 和 RDB 保证了前者,而对于后者,Redis 的做法是增加副本冗余量。将一份数据同时保存在多个实例上。这样,即使有一个实例出现了故障,需要果断时间才能恢复,其他实例也可以对外提供服务,不影响业务使用。

Redis 提供了主从库模式,以保证数据副本的一致,主从库之间采用的是读写分离的方式。

  • 读操作:主库、从库都可以接收
  • 写操作:首先到主库执行,然后,主库将写操作同步给从库。

为什么采用读写分离的方式?
设想一下,如果不管是主库还是从库,都能接收客户端的写操作,那么,一个直接的问题就是:如果客户端对同一个数据(例如 k1)前后修改了三次,每一次修改请求都发送到不同的实例上执行,那么,这个数据在这三个实例上的副本就不一致了。在读取这个数据的时候,就可能读到旧值。
如果我们非要保持这个数据在三个实例上一致,就涉及加锁、实例间协商是否完成修改等一些列操作,但这会带来巨额的开销,当然是不能接受的。
而主从模式一旦采用了读写分离,所有数据的修改只会在主库上进行,不用协调三个实例。主库有了最新的数据后,会同步给从库,这样,主从库的数据就是一致的。

主从库同步是如何完成的?主库是一次性传给从库,还是分批同步?要是主从库网络断连了,数据还能保持一致吗?

我们先来看下主从库间的第一次同步是如何进行的,这也是 Redis 实例建立主从库模式后的规定动作。

1.1 主从库间如何进行第一次同步?

当我们启动多个 Redis 实例的时候,它们之间就可以通过 replicaof (Redis 5.0 之前使用 slaveof)命令形成主从库的关系,之后会按照三个阶段完成数据的第一次同步。

例如,现在有实例 1(ip:172.16.19.3)和实例 2(ip:172.16.19.5),我们在实例2上执行以下这个命令后,实例 2 就变成了实例 1 的从库,并从实例 1 上复制数据:

replicaof 172.16.19.3 6379

接下来,主从数据库间数据的第一次同步的三个阶段了。先看下图
在这里插入图片描述

第一阶段

第一阶段是主从库建立连接、协商同步的过程,主要是为全量复制做准备。在这一步,从库和主库建立起连接,并告诉主库即将进行同步,主库确认回复后,主从库间就可以开始同步了

具体来说,从库给主库发送 psync 命令,表示要进行数据同步,主库根据这个命令的参数来启动复制。psync 命令包含了主库的 runID复制进度两个参数。

  • runID 是每个 Redis 实例启动时都会自动生成的一个随机 ID,用来唯一标记这个实例。当从库和主库第一次复制时,因为不知道主库的 runID,所以将 runID 设为 “?”。
  • offset,此时设为 -1,表示第一次复制。

主库收到 psync 命令后,会用 FULLRESYNC 响应命令带上两个参数:主库 runID 和主库目前的复制进度 offset,返回给从库。从库收到响应后,会记录下这两个参数。

有个地方需要注意, FULLRESYNC 响应表示第一次复制采用的全量,也就是说,主库会把当前所有的数据都复制给从库

第二阶段

在第二阶段,主库将所有数据同步给从库。从库收到数据后,在本地完成数据加载。这个过程依赖于内存快照 RDB 文件。

具体来说,主库执行 bgsave 命令,生成 RDB 文件,接着将文件发给从库。从库接收到 RDB 文件后,会先清空当前数据库,然后加载 RDB 文件。这是因为从库可能保存了其他数据,为了避免之前数据的影响,从库需要先把当前数据库清空。

在主从同步过程中,仍然可以正常接收请求。否则,Redis 的服务就被中断了。但是,这些请求中的写操作并没有记录到刚刚生成的 RDB 文件中。为了保证主从库数据一致性,主库会在内存中用专门的 replication buffer,记录 RDB 文件生成后收到的所有写操作。

第三阶段

最后,也就是第三个阶段,主库会把第二阶段执行过程中新收到的写命令,再发送给从库。具体的操作是,当主库完成 RDB 文件发送后,就会把此 replication buffer 中的修改操作发给从库,从库再重新执行这些操作。这样一来主从库就实现同步了。

1.2 主从级联模式分担全量复制时的主库压力

通过分析主从库间第一次数据同步的过程,你可以看到,一次全量复制中,对于主库来说,需要完成两个耗时的操作:生成 RDB 文件和传输 RDB 文件。

如果从库数量很多,而且都要和主库进行全量复制的话,就会导致主库忙于 fork 子进程生成 RDB 文件,进行全量数据同步。fork 这个操作会阻塞主线程处理正常请求,从而导致主库响应应用程序的请求速度变慢。此外,传输 RDB 也会占用主库的网络带宽,同样会给主库的资源使用带来压力。那么,有没有好的解决方法可以分担主库压力呢?

其实是有的,这就是“主 - 从 - 从”模式。

上面介绍的主从库模式中,所有的从库都是和主库连接,所有的权力复制也都是和主库进行的。现在,我们可以通过“主 - 从 - 从”模式将主库生成 RDB 和传输 RDB 的压力,以级联的方式分散到从库上

简单来说,我们在部署主从集群的时候,可以手动选择一个从库(比如选择资源配置较高的从库),用于级联其他的从库。然后,我们可以再选择一些从库(例如三分之一的从库),在这些从库上执行如下命令,让它们和刚才所选的从库,建立起主从关系。

replicaof 所选从库的IP 6379

这样一来,这些从库就会知道,在进行同步时,不用再和主库进行交互了,只要和级联的丛库进行写操作同步就行了,这就可以减轻主库上的压力,如下图所示:
在这里插入图片描述
到这里,我们了解了主从库间通过全量复制实现数据同步的过程,以及通过“主 - 从 - 从”模式分担主库同步压力的方式。那么,一旦主从库完成了全量复制,它们之间就会一直维护一个网络连接,主库会通过这个连接将后续收到的写操作再同步给从库,这个过程也称基于长连接的命令传播,可以避免频繁建立连接的开销。

在这个过程中存在风险点,最常见的是网络断连或阻塞。如果网络断连,主从库之间就无法进行命令传播了,从库的数据自然也就没办法和主库保持一致了,客户端也就可能从从库中读到旧数据。

1.3 主从库间网络断了怎么办?

早 Redis 2.8 之前,如果主从库在命令传播时出现了网络闪断,那么,从库就会和主库重新进行一次全量同步,开销非常大。

从 Redis 2.8 开始,网络断了之后,主从库会采用增量复制的方式继续同步。增量复制只会把主从库网络断连期间主库收到的命令,同步给从库。

增量复制时,主从库之间是怎么保持同步的?是基于 repl_backlog_buffer 这个缓冲区。

当主从库断连后,主库会把断连期间收到的写操作,写入 replication buffer,同时也会把这些操作也写入 repl_backlog_buffer 缓冲区。

repl_backlog_buffer 是一个环形缓冲区,主库会记录自己写到的位置,从库则会记录自己已经读到的位置

刚开始的时候,主库与从库的写读位置在一起,这算是它们的起始位置。随着主库不断接收新的写操作,它在缓冲区中的写位置会逐步偏离起始位置,我们通常用偏移量来衡量这个便宜距离的大小,对主库来说,对应的偏移量就是 master_repl_offset。主库接收的新写操作越多,这个值就越大。

同样,从库在复制写完操作命令后,它在缓冲区中的读位置也开始逐步偏移刚才的起始位置,此时,从库已复制的偏移量 slave_repl_offset 也在不断增加。正常情况下,这两个便宜量基本相等。
在这里插入图片描述
主从库的连接回复之后,首先会给主库发送 psync 命令,并把自己当前的 slave_repl_offset 发送给主库,主库会判断自己的 master_repl_offset 和 slave_repl_offset 之间的差距。

在网络断连阶段,主库可能会收到新的写操作命令,所以,一般来说,master_repl_offset 会大于 slave_repl_offset。此时,主库只用把 master_repl_offset 和 slave_repl_offset 之间的命令操作同步给从库就行。

就像刚刚示意图的中间部分,主库和从库之间相差了 put d e 和 put d f 两个操作,在增量复制时,主库只需要把它们同步给从库就行了。

说到这里,我们再借助一张图,回顾下增量复制的流程。
在这里插入图片描述
不过,因为 repl_backlog_buffer 是一个环形缓冲区,所以在缓冲区写满后,主库会继续写入,此时,就会覆盖之前写入的操作。如果从库的读取速度比较慢,就有可能导致从库还未读取的操作被主库新鞋的操作覆盖了,这会导致主从库间的数据不一致

我们要想办法避免这一情况,一般而言我们可以调整 repl_backlog_size 这个参数。缓冲空间的计算公式是: 缓冲空间大小 = 主库写入命令速度 * 操作大小 - 从库网络传输命令速度 * 操作大小。 在实际应用中,考虑到可能存在一些突发的请求压力,通常把这个缓存空间扩大一倍,即 repl_backlog_size = 缓冲空间大小 * 2,这也就是 repl_backlog_size 的最终值。

举个例子,如果主库每秒写入 2000 个操作,每个操作的大小为 2KB,网络每秒能传输 1000 个操作,那么,有 1000 个操作需要缓冲起来,这就至少需要 2MB 的缓冲空间。否则,新写的命令就会覆盖掉就操作了。为了应对可能的突发压力,我们最终把 repl_backlog_size 设为 4MB。

这样一来,增量复制时主从库的数据风险不一致风险就降低了。不过,如果并发请求量非常大,连两倍的缓冲空间都存不下新操作的话,此时,主从库数据仍然可能不一致。

针对这种情况,一方面,你可以根据 Redis 所在服务器的内存资源再适当增加 repl_backlog_size 值,比如说设置成缓冲空间大小的 4 倍,另外一方面,你可以考虑使用切片集群来分担单个主库的请求压力。

1.4 总结

总体来说,Redis 得到主从库同步的基本原理,有三种模式:全量同步、基于长链接的命令传播,以及增量同步。

全量复制虽然耗时,但是对于从库来说,如果是第一次同步,全量复制是无法避免的,所以,建议一个 Redis 的数据量不要太大,一个实例的大小在几 GB 级别比较合适,这样可以减少 RDB 文件生成、传输和重新加载的开销。另外,为了避免多个从库同时和主库进行全量复制,给主库过大压力,可以采用“主 - 从 - 从”级联模式,来缓解主库压力。

2. 哨兵机制:主库挂了,如何不间断服务?

我们知道在主从集群模式下,如果从库发生故障,客户端可以继续向主库或其他从库发送请求,但如果主库发生故障了,那就直接会影响到从库的同步,因为从库没有相应的主库可以进行数据复制操作。

如果客户端发送的都是读操作请求,那还可以由从库继续服务。但是,一旦有写操作请求了,此时没有实例可以来服务客户端的写请求,如下所示:
在这里插入图片描述
无论是写服务中断,还是从库无法进行数据同步,都是不能接受的。所以,若果主库挂了,我们需要运行一个新主库,比如把一个从库切换为主库。这设计到三个问题:

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

在 Redis 主从集群中,哨兵机制是实现主从库自动切换的关键机制。

2.1 哨兵机制的基本流程

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

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

然后就开始自动切换主库的流程,这是哨兵的第二个任务,选主。主库挂了后,哨兵就需要从多个从库中,按照一定的规则选择一个从库,把它作为新主库。这一步完成后,现在的集群里就有了新主库。

然后,哨兵会执行最后一个任务:通知。在执行通知任务时,哨兵会把新主库的连接信息发送给其他从库,让它们执行 replicaof 命令,和新主库建立连接,并进行数据复制。同时,哨兵会把新主库的连接信息通知给客户端,让它们把请求操作发到新主库上。
在这里插入图片描述
在这三个任务中,通知任务相对来水比较简单,哨兵只需要把新主库信息发送给从库和客户端,让它们和新主库建立连接就行,并不涉及决策的逻辑。但是在监控和选主的任务中,哨兵需要做出两个决策:

  1. 在监控任务中,哨兵需要判断主库是否处于下线状态
  2. 在选主任务中,哨兵需要选择哪个从库作为主库

接下来先看下哨兵如何判断主库的下线状态。

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

2.2 主观下线和客观下线

先来解释下什么是主观下线

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

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

但是,如果检测的是主库,哨兵不能简单的标记为“主观下线”,就开启主从切换。因为有可能是哨兵误判了,其实主库并没故障。

因为一旦启动了主从切换,后续的选主、新主从库间的数据同步和通知操作都会带来额外的计算和通信开销。

为了避免这些不必要的麻烦,要注意避免误判的情况。

误判一般发生在集群网络压力较大、网络拥堵,或者是主库本身压力较大的情况下。

如何减少误判呢? 在日常生活这,我们要对一些重要的事情做判断的时候,经常和家人或朋友一起商量下,然后再做决定。哨兵机制也是类似的,它通常采用多实例组成的集群模式进行部署,这也被称为哨兵集群。引入多个哨兵一起来判断,就可以避免单个哨兵因自身网络状况不好,而误判主库下线的情况。同时,多个哨兵的网络同时不稳定的概率较小,由他们一起做决策,误判率也能极大降低。

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

我们举个例子,如下图所示, Redis 主从集群有一个主库、三个从库,还有三个哨兵实例。
在图片的左边:

  • 哨兵 2 判断主库“主观下线”
  • 哨兵 1、3 却判定主库是上线状态
  • 此时,主库仍然会被判定处于上线状态。

在图片右边:

  • 哨兵 1、2 判断主库“主观下线”
  • 此时,即使哨兵 3 判定主库是上线状态,主库也被标记为“客观下线”了

在这里插入图片描述
简而言之,“客观下线”的标准是,当有 N 个哨兵实例,最好要有 N/2 + 1 个实例判断主库为“主观下线”,才能最终判定主库为“客观下线”。

2.3 如何选定新主库?

哨兵选择新主库的过程称为“筛选 + 打分”。

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

一定的筛选条件

首先,我们要把当前下线的从库筛掉。我们肯定要先保证所选的从库仍然在线。不过在选主时从库正常在线,只能表示从库的现状良好,并不代表它就是最适合做新主库的。

其次,除了要检查从库的当前在线状态,还要判断它之前的网络连接状态。若从库总是和主库断连,而且断连次数超过了一定的阈值,就可以把这个从库筛掉了。

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

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

一定的规则

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

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

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

第二轮:和旧主库同步程度最接近的从库得分高
我们知道主从库同步时有个命令传播的过程。在这个过程中,主库会用 master_repl_offser 记录当前的最新写操作在 repl_backlog_buffer 中的位置,而从库会用 slave_repl_offset 这个值记录当前的复制进度。

此时,我们想要找的从库,它的 slave_repl_offser 需要最接近 master_repl_offset。如下图所示,旧主库的 master_repl_offset 是 1000,从库 1、2、3 的 salve_repl_offset 分别是 950、990、900,那么从库 2 就应该被选为主库。
在这里插入图片描述
当然,如果从库的 slave_repl_offset 值大小是一样的,我们就需要给它们进行第三轮打分了。

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

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

到这里,新主库就被选出来了。

小结

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

3. 哨兵集群:哨兵挂了,主从库还能切换吗?

第2节,我们学习了哨兵机制,它可以实现主从库的自动切换。通过部署多个哨兵实例,就形成了一个哨兵集群。哨兵集群中的多个实例共同判断,可以降低对主库下线的误判。

还需要考虑一个问题:如果有哨兵实例在运行时发生了故障,主从库还能正常切换吗?

实际上,一旦多个实例组成了哨兵集群,即使有哨兵实例出现故障挂掉了,其他哨兵还能继续协作完成主从库的切换工作,包括判定主库是不是处于下线状态、选新主库、通知从库和客户端。

如果你部署过哨兵集群的话就会知道,在配置哨兵的信息时,我们只需要用到下面的配置,设置主库 IP端口,并没有配置其他的哨兵信息。

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

这些哨兵实例既然都不知道彼此的地址,又是怎么组成集群的呢?要弄明白这个问题 ,我们要学习下哨兵集群的组成和运行机制了。

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

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

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

Redis 以频道的形式区分不同应用的消息,并对这些消息进行分门别类的管理。所谓频道就是消息的类别。当消息类别相同时,它们就属于同一个频道。只有订阅了同一个频道的应用,才能通过发布的消息进行消息交换

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

举个例子。在下图中:

  1. 哨兵 1 把自己的 IP(172.16.19.3) 和端口(26579)发布到 “__sentinel__:hello”频道上。
  2. 哨兵 2、3 订阅了该频道。那么此时,哨兵 2、3 就从可从这个频道直接获取哨兵 1 的 IP 和端口号。
  3. 然后,哨兵 2、3 和哨兵 1 建立网络连接。
  4. 通过这个方式哨兵 2、3 也可以建立网络连接。

这样一来哨兵集群就形成了。它们可以通过网络连接进行通信,比如过主库有没有下线这件事儿进行协商。
在这里插入图片描述
哨兵除了彼此之间建立起连接形成集群外,还需要和从库建立连接。这是因为,在哨兵的监控任务中,它需要对主从库都进行心跳判断,而且在主从库切换完成后,它还需要通知从库,让他们和新主库进行同步。

哨兵是如何知道从库的 IP 地址和端口的呢?
这是通过哨兵向主库发送 INFO 命令来完成的。就像下图所示:

  1. 哨兵 2 给主库发送 INFO 命令,主库接受到这个命令后,就会把从库列表返回给哨兵。
  2. 接着,哨兵就可以根据从库列表中的连接信息,和每个从库建立连接,并在这个连接上持续的对从库进行监控。
  3. 哨兵 1、3 通过相同的方法和从库建立连接。
    在这里插入图片描述
    通过 pub/sub 机制,哨兵之间可以组成集群,同时哨兵又通过 INFO 命令,获取了从库连接信息,也能和从库建立连接,并进行监控了。

但是,哨兵不能之和主、从库连接。因为,主从库切换后,客户端也需要知道新主库的连接信息,才能向新主库发送请求操作。所以,哨兵还需要完成把新主库的信息告诉各个客户端这个任务。

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

此时,我们仍可以通过 pub/sub 机制,来帮助我们完成哨兵和客户端间的信息同步。

3.2 基于 pub/sub 机制的客户端事件通知

每个哨兵实例也提供了 pub/sub 机制,客户端可以从哨兵订阅消息。哨兵提供的消息频道有很多,不同频道包含了主从库切换过程中的不同关键事件。

在这里插入图片描述
知道了这些频道知乎,你就可以让客户端从哨兵这里订阅消息了。具体的操作步骤是,客户端读取哨兵的配置文件后,可以获得哨兵的地址和端口,和哨兵建立网络连接。然后,我们可以在客户端执行订阅命令来获取不同的时间消息。

举个例子,你可以执行如下命令,来订阅“所有实例进入客观下线状态的事件”:

SUBSCRIBE +odown

当然,你可以可以执行如下命令,订阅所有的时间:

PSUBCRIBE *

当哨兵把新主库选择出来后,客户端就会看到下面的 switch-master 事件。这个事件表示主库已经切换了,新主库的 IP 地址和端口信息已经有了。这个时候,客户端就可以用这里的新主库地址和端口进行通信了:

Switch-master <master name> <oldip> <oldport> <newip> <newport>

有了这些事件通知,客户端不仅可以在主存切换后得到新主库的连接信息,还可以监控主从库切换过程中发生的各个重要事件。这样,客户端就可以知道主从切换进行到哪一步了,有助于了解切换进度。

还有一个问题就是,主库故障以后,哨兵集群中有多个实例,那么怎么确定由哪个哨兵进行实际的主从切换呢?

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

确定由哪个哨兵执行主从切换的过程,和主库“客观下线”的判断过程类似,也是一个投票仲裁的过程。在了解这个过程之前,我们先来看下,判断“客观下线”的仲裁过程。

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

在这里插入图片描述

一个哨兵获得了仲裁所需的赞成票后,就可以标注“客观下线”。这个所需的赞成票数是通过哨兵配置文件中的 quorum 配置项设定的。例如,现有 5 个哨兵,quorum 配置的是 3,那么,一个哨兵需要 3 张赞成票,就可以标记主库为 “客观下线”了。这 3 张赞成票包括哨兵自己的一张赞成票和另外两个哨兵的赞成票。

此时,这个哨兵就可以给其他哨兵发送命令,表明希望由自己来执行主从切换,并让所有其他哨兵进行投票。这个投票过程为“Leader 选举”。因为最终执行主从切换的哨兵称为 Leader,投票过程就是确定 Leader。

在投票过程中,任何一个想称为 Leader 的哨兵,要满足两个条件:

  1. 第一,拿到半数以上的赞成票
  2. 第二,拿到的票数还需要大于等于哨兵配置文件的 quorum 的值。以3个哨兵为例,假设此时 quorum 为 2,任何一个想称为 Leader 的哨兵只要拿到 2 张赞成票,就可以了。

再画一张图,展示下 3 个哨兵、 quorum 为 2 的选举过程。
在这里插入图片描述

  • T1 时刻, S1 判断主库为“客观下线”,它想称为 Leader,就先给自己投一张赞成票,然后分别向 S2、S3 发生命令,表示要称为 Leader。
  • T2 时刻,S3 判断主库为“客观下线”,它想称为 Leader,也先给自己投一张赞成票,然后分别向 S1、S2 发生命令,表示要称为 Leader。
  • T3 时刻,S1 收到了 S3 的 Leader 投票请求。因为 S1 已经给自己投了一票,所以它不能再给其他哨兵投赞成票,所以恢复 N 表示不同意 S3 成为 Leader。
    同时, S2 收到了 T2 时刻 S3 发送的 Leader 投票请求。因为 S2 之前没有投过票,它会给第一个向他发送投票请求的哨兵恢复 Y,给后续再发送投票请求的哨兵恢复 N。所以在 T3 时刻,S2 同意 S3 称为 Leader。
  • 在 T4 时刻,S2 才收到 S1 发送的投票命令,此时 S2 给 S1 回复 N ,表示不同意 S1 成为 Leader。发生这种情况,是因为 S3 和 S2 之间网络传输正常,而 S1 和 S2 之前的网络传输可能正好堵塞了,导致投票请求传输慢了。
  • 最后,在 T5 时刻,最终 S1 只有一个赞成票,而 S3 有两个赞成票。此时,S3 不仅获得了半数以上的 Leader 赞成票,也达到预设的 quorum 值(quorum 为 2),所以它最终称为了 Leader。接着 S3 开始执行选主操作,而且选定新主库后,会给其他客户端通知新主库信息。

如果 S3 也没有拿到 2 票 Y,那么这轮投票就不会产生 Leader。哨兵集群会等待一段时间,在重新选举。这是因为,哨兵集群能够进行成功投票,很大程度上依赖于选举命令的正常网络传播。如果网络压力较大或有较短的堵塞,就可能导致没有一个哨兵能拿到半数以上的赞成票。所以,等到网络拥塞好转之后,再进行投票选举,成功的概率就会增加。

需要注意的是,如果哨兵集群只有2个实例,此时,一个哨兵要想成为 Leader,必须获得 2 票,而不是 1 票。所以,如果有个哨兵挂掉了,那么此时的集权是无法进行主从库切换的。因此,我们至少会配置 3 个哨兵实例。这一点很重要,你在实际应用时可不能忽略了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1415784.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

qemu单步调试arm64 linux kernel

一、背景和目的 qemu搭建arm64 linux kernel环境-CSDN博客 之前介绍了qemu启动kernel的配置步骤和方法&#xff0c;现在开始我们的调试&#xff0c;这篇文章主要讲解如何单步调试内核&#xff0c;所有的实验还是基于ARM64&#xff1b; 二、环境准备 需要准备hostx86 target…

vue2中的watch(侦听器)讲解,以及解决深度监听新值和旧值相同的两种方案(手写深拷贝和JSON.parse())。

目录 一&#xff1a;什么是watch? 二&#xff1a;watch的基础使用 1.最基本的使用 2.简写形式 三&#xff1a;watch中的immediate和deep属性 1.immediate属性 2.deep属性 3.解决深度监听新旧值相同的问题 1&#xff09;使用序列化和反序列化。 2&#xff09;手写深…

DevOps系列文章之 GitLabCI汇总

GitlabCI环境搭建 前提 先安装 docker Docker容器化安装 docker pull gitlab/gitlab-ee:12.4.0-ee.0 创建挂载目录 mkdir -p /srv/gitlab mkdir -p /srv/gitlab/config # 映射到 Glitlab 容器中的配置目录 mkdir -p /srv/gitlab/logs # 映射到 Glitlab 容器中的日志目录 m…

换个思维方式快速上手UML和 plantUML——类图

和大多数朋友一样&#xff0c;Jeffrey 在一开始的时候也十分的厌烦软件工程的一系列东西&#xff0c;对工程化工具十分厌恶&#xff0c;觉得它繁琐&#xff0c;需要记忆很多没有意思的东西。 但是之所以&#xff0c;肯定有是因为。对工程化工具的不理解和不认可主要是基于两个逻…

最新国内GPT4.0使用教程,AI绘画-Midjourney绘画V6 ALPHA绘画模型,GPT语音对话使用,DALL-E3文生图+思维导图一站式解决方案

一、前言 ChatGPT3.5、GPT4.0、GPT语音对话、Midjourney绘画&#xff0c;文档对话总结DALL-E3文生图&#xff0c;相信对大家应该不感到陌生吧&#xff1f;简单来说&#xff0c;GPT-4技术比之前的GPT-3.5相对来说更加智能&#xff0c;会根据用户的要求生成多种内容甚至也可以和…

围棋的气 - 华为OD统一考试

OD统一考试&#xff08;C卷&#xff09; 分值&#xff1a; 100分 题解&#xff1a; Java / Python / C 题目描述 围棋棋盘由纵横各19条线垂直相交组成&#xff0c;棋盘上一共19x19361个交点&#xff0c;对弈双方一方执白棋&#xff0c;一方执黑棋&#xff0c;落子时只能将棋子…

智慧文旅:未来旅游业的数字化转型

随着科技的快速发展&#xff0c;数字化转型已经成为各行各业的必然趋势。旅游业作为全球经济的重要组成部分&#xff0c;也正经历着前所未有的变革。智慧文旅作为数字化转型的重要领域&#xff0c;正逐渐改变着旅游业的传统模式&#xff0c;为游客带来更加便捷、个性化的旅游体…

锐龙笔记本Windows 11休眠无法唤醒问题的解决(6800h, 7840H/Hs)

锐龙笔记本运行Windows 11时经常会遇到休眠后无法唤醒的问题&#xff0c;表现为休眠后 按键盘或鼠标无反应&#xff0c;只能长按电源开关关机后再开机。网上有很多说法&#xff0c;比如显卡问题或其它问题。但是本质 上这个是电源管理软硬件不兼容导致的&#xff0c;解决办法如…

2024 年 eBPF 和网络趋势预测

本文地址&#xff1a;2024 年 eBPF 和网络趋势预测 | 深入浅出 eBPF 1. eBPF 1.1 eBPF 将继续呈指数增长1.2 eBPF 应用市场1.3 eBPF 在手机中得到更广泛的应用1.4 eBPF 滥用带来的风险2. 可观测 2.1 最受欢迎的可观测性2.2 降低可观测性开销2.3 上下文感知的 Kubernetes 工作负…

python222网站实战(SpringBoot+SpringSecurity+MybatisPlus+thymeleaf+layui)-自定义帖子管理实现

锋哥原创的SpringbootLayui python222网站实战&#xff1a; python222网站实战课程视频教程&#xff08;SpringBootPython爬虫实战&#xff09; ( 火爆连载更新中... )_哔哩哔哩_bilibilipython222网站实战课程视频教程&#xff08;SpringBootPython爬虫实战&#xff09; ( 火…

力扣516. 最长回文子序列

动态规划 思路&#xff1a; 字符串最长回文子序列问题可以转换为原字符串 s 和逆串 s 的最长公共子序列长度问题&#xff0c;具体推断过程可以参考 力扣1312. 让字符串成为回文串的最少插入次数问题变成了求两个字符串最长公共子序列长度问题&#xff0c;具体思路可以参考 力扣…

每次请求sessionid变化【SpringBoot+Vue】

引言&#xff1a;花了一晚上的时间&#xff0c;终于把问题解决了&#xff0c;一开始后端做完后,用apifox所有接口测试都是可以的,但当前端跑起来后发现接收不到后端的数据。 当我写完前后端&#xff0c;主页面和获取当前页面信息接口后&#xff0c;配置了cros注解 CrossOrigin…

数据湖技术之发展现状篇

一. 大数据处理架构&#xff1a; 大数据处理架构的发展过程具体可以分为三个主要阶段&#xff1a;批处理架构、混合处理架构&#xff08;Lambda、Kappa架构&#xff09;、湖仓一体。首先是随着Hadoop生态相关技术的大量应用&#xff0c;批处理架构应运而生&#xff0c;借助离线…

QEMU源码全解析41 —— Machine(11)

接前一篇文章&#xff1a;QEMU源码全解析40 —— Machine&#xff08;10&#xff09; 本文内容参考&#xff1a; 《趣谈Linux操作系统》 —— 刘超&#xff0c;极客时间 《QEMU/KVM》源码解析与应用 —— 李强&#xff0c;机械工业出版社 特此致谢&#xff01; 时间过去了几…

MCU启动文件小解一下

GD32启动文件分析 启动文件的一些指令.s启动文件分析栈空间分配堆空间管理中断向量表定义堆空间定义Reset_Handler复位程序HardFault_Handler_main文件分析用户堆栈初始化 GD32启动文件主要做了以下工作&#xff1a; 初始化SP_initial_sp , PCReset_Handler指针&#xff0c;设置…

眼底增强型疾病感知蒸馏模型 FDDM:无需配对,fundus 指导 OCT 分类

眼底增强型疾病感知蒸馏模型 FDDM&#xff1a;fundus 指导 OCT 分类 核心思想设计思路训练和推理 效果总结子问题: 疾病特定特征的提取与蒸馏子问题: 类间关系的理解与建模 核心思想 论文&#xff1a;https://arxiv.org/pdf/2308.00291.pdf 代码&#xff1a;https://github.c…

C#调用SqlSugar操作达梦数据库报错“无效的表或视图名”

安装达梦数据库后&#xff0c;使用SqlSugar连接测试数据库并基于DBFirst方式创建数据库表对应的类&#xff0c;主要代码如下&#xff1a; SqlSugarClient db new SqlSugarClient(new ConnectionConfig(){DbType DbType.Dm,ConnectionString "Serverlocalhost; User Id…

Redis3-秒杀活动

秒杀 准备工作 我是参照下面这位大佬的i骄傲成下载的 csdn友情链接 Jmeter模拟多线程的压力测试工具 秒杀代码&#xff1a; package com.aaa.controller;import io.netty.util.internal.StringUtil; import org.apache.commons.lang.StringUtils; import org.springfram…

YoloV8改进策略:BackBone改进|DCNv4最新实践|高效涨点|多种改进教程|完整论文翻译

摘要 涨点效果:在我自己的数据集上,mAP50 由0.986涨到了0.993,mAP50-95由0.737涨到0.77,涨点明显! DCNv4是可变形卷积的第四版,速度和v3相比有了大幅度的提升,但是环境搭建有一定的难度,对新手不太友好。如果在使用过程遇到编译的问题,请严格按照我写的环境配置。 …

【GitHub项目推荐--不错的 TypeScript 学习项目】【转载】

在线白板工具 Excalidraw 标星 33k&#xff0c;是一款非常轻量的在线白板工具&#xff0c;可以直接在浏览器打开&#xff0c;轻松绘制具有手绘风格的图形。 如下图所示&#xff0c;Excalidraw 支持最常用的图形元素&#xff1a;方框、圆、菱形、线&#xff0c;可以方便的使用…