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

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.mzph.cn/news/651849.shtml

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

相关文章

RTP工具改进(五)--使用qt

前篇 第四篇 RTP工具改进(四) - rtmp协议推送 前面使用的工具一直为mfc&#xff0c;今天将使用qt 来做界面&#xff0c;使用qt 来进行程序和协议的编写&#xff0c;qt部分目前还不包括rtp ps流和rtmp&#xff0c;暂时只有rtp 直接传输&#xff0c;关于rtmp协议和ps流协议&…

在 VUE 项目中,使用 Axios 请求数据时,提示跨域,该怎么解决?

在 VUE 项目开发时&#xff0c;遇到个问题&#xff0c;正常设置使用 Axios 库请求数据时&#xff0c;报错提示跨域问题。 那在生产坏境下&#xff0c;该去怎么解决呢&#xff1f; 其可以通过以下几种方式去尝试解决&#xff1a; 1、设置允许跨域请求的响应头 1.1 在响应头中…

STM32实现软件IIC协议操作OLED显示屏(2)

时间记录&#xff1a;2024/1/27 一、OLED相关介绍 &#xff08;1&#xff09;显示分辨率128*64点阵 &#xff08;2&#xff09;IIC作为从机的地址0x78 &#xff08;3&#xff09;操作步骤&#xff1a;主机先发送IIC起始信号S&#xff0c;然后发送OLED的地址0x78&#xff0c;然…

机器学习的数据库积累........

https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/tf1_detection_zoo.md ​​​​​​​ 另一个database:&#xff08;网址:Object Detection Made Easy with TensorFlow Hub: Tutorial&#xff09; Object Detection Made Easy with Ten…

VR拍摄+制作

1.VR制作需要的图片宽高是2:1&#xff0c;需要360✖️180的图片&#xff0c;拍摄设备主要有两种&#xff1a; 1&#xff09;通过鱼眼相机拍摄&#xff0c;拍摄一组图片&#xff0c;然后通过PTGui来合成(拍摄复杂) 2&#xff09;全景相机&#xff0c;一键拍摄直接就能合成需要的…

【动态规划】【逆向思考】【C++算法】960. 删列造序 III

作者推荐 【动态规划】【map】【C算法】1289. 下降路径最小和 II 本文涉及知识点 动态规划汇总 LeetCode960. 删列造序 III 给定由 n 个小写字母字符串组成的数组 strs &#xff0c;其中每个字符串长度相等。 选取一个删除索引序列&#xff0c;对于 strs 中的每个字符串&a…

群晖nas 中的 VideoStation 关于豆瓣刮刮和TheMovieDb无法链接问题

网上各种教学文档很多&#xff0c;但是都有各种的坑&#xff0c;这偏文章仅对坑进行修正&#xff0c;具体可以参考其他文章&#xff0c;不同点以此文章为准。 第一部分&#xff0c;豆瓣刮刮 是 VideoStation的最佳搭配&#xff0c;汉字匹配&#xff0c;速度肯定是TheMovieDb无…

贾玲新片刚刚发出紧急声明,是什么情况。

♥ 为方便您进行讨论和分享&#xff0c;同时也为能带给您不一样的参与感。请您在阅读本文之前&#xff0c;点击一下“关注”&#xff0c;非常感谢您的支持&#xff01; 文 |猴哥聊娱乐 编 辑|徐 婷 校 对|侯欢庭 1月22日&#xff0c;一则“多位明星参演的电影涉影视投资诈骗…

[晓理紫]每日论文分享(有中文摘要,源码或项目地址)--强化学习、模仿学习、机器人、开放词汇

专属领域论文订阅 关注{晓理紫|小李子}&#xff0c;每日更新论文&#xff0c;如感兴趣&#xff0c;请转发给有需要的同学&#xff0c;谢谢支持 如果你感觉对你有所帮助&#xff0c;请关注我&#xff0c;每日准时为你推送最新论文。 分类: 大语言模型LLM视觉模型VLM扩散模型视觉…

STM32 freertos 使用软件模拟串口uart

如题&#xff0c;为什么要这样做&#xff1f; 最近做的一个项目上使用了74HC595作为指示灯板使用&#xff1b; 这个灯板与驱动板是通过排线连接&#xff0c;排线约25cm长&#xff1b; 在实验室测试一切正常&#xff0c;发到客户手上使用就出现了某个LED跳动情况&#xff1b;…

GitHub 一周热点汇总第7期(2024/01/21-01/27)

GitHub一周热点汇总第7期 (2024/01/21-01/27) &#xff0c;梳理每周热门的GitHub项目&#xff0c;离春节越来越近了&#xff0c;不知道大家都买好回家的票没有&#xff0c;希望大家都能顺利买到票&#xff0c;一起来看看这周的项目吧。 #1 rustdesk 项目名称&#xff1a;rust…

解决Linux部署报错No main manifest attribute, in XXX.jar

这是我近期遇到的一个问题&#xff0c;报错原因就是没找到主类&#xff0c;首先你在你本地运行&#xff0c;本地运行ok的话&#xff0c;解压生成的jar包&#xff0c;里面有个META-INF文件&#xff0c;打开MANIFEST.MF文件&#xff0c;该文件是一个清单文件。该文件包含有关JAR文…

vulnhub靶场之Five86-2

一.环境搭建 1.靶场描述 Five86-2 is another purposely built vulnerable lab with the intent of gaining experience in the world of penetration testing. The ultimate goal of this challenge is to get root and to read the one and only flag. Linux skills and fa…

vusui css 使用,简单明了 适合后端人员 已解决

vusui-cssopen in new window 免除开发者繁复的手写 CSS 样式&#xff0c;让 WEB 前端开发更简单、灵活、便捷&#xff01;如果喜欢就点个 ★Staropen in new window 吧。 移动设备优先&#xff1a; vusui-css 包含了贯穿于整个库的移动设备优先的样式。浏览器支持&#xff1a…

三数之和----双指针

https://leetcode.cn/problems/3sum/description/?envType=study-plan-v2&envId=top-100-liked “三数之和”在某些人的口中被叫做“程序员之梦破碎的地方”。既然如此,这个题肯定是有难度的,尤其是其中的细节,很多,很细。 其中nums代表给定的数组,numsSize代表给定数…

GLog开源库使用

Glog地址&#xff1a;https://github.com/google/glog 官方文档&#xff1a;http://google-glog.googlecode.com/svn/trunk/doc/glog.html 1.利用CMake进行编译&#xff0c;生成VS解决方案 &#xff08;1&#xff09;在glog-master文件夹内新建一个build文件夹&#xff0c;用…

【JAVA】什么是自旋

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;JAVA ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 结语 我的其他博客 前言 在计算机科学的领域中&#xff0c;多线程和并发编程已成为处理复杂任务和提高系统性能的不可或缺的手段。…

【时间序列篇】基于LSTM的序列分类-Pytorch实现 part2 自有数据集构建

系列文章目录 【时间序列篇】基于LSTM的序列分类-Pytorch实现 part1 案例复现 【时间序列篇】基于LSTM的序列分类-Pytorch实现 part2 自有数据集构建 【时间序列篇】基于LSTM的序列分类-Pytorch实现 part3 化为己用 在一个人体姿态估计的任务中&#xff0c;需要用深度学习模型…

深度学习(6)--Keras项目详解

目录 一.项目介绍 二.项目流程详解 2.1.导入所需要的工具包 2.2.输入参数 2.3.获取图像路径并遍历读取数据 2.4.数据集的切分和标签转换 2.5.网络模型构建 2.6.绘制结果曲线并将结果保存到本地 三.完整代码 四.首次运行结果 五.学习率对结果的影响 六.Dropout操作…

一个基于electron自动化桌面应用-流程图构建

前期工作已搞定&#xff0c;现在可以搭建桌面应用了。这个阶段可以结合前面定义好的数据格式构建流程图。 模板 还是使用熟悉的技术栈vite react electron&#xff0c;模板 流程图 官方文档 自定义 节点样式 因为配置化的操作类型较多&#xff0c;因此可以利用自定义节…