Redis进阶知识05-哨兵机制

由 Geooo 发布于 February 18, 2022

Redis哨兵机制

在主从模式下解决了主库的读压力,如果从库挂了,客户端还能继续向主库或者其他从库发送命令请求,但是如果主库出故障挂了就会导致以下两个问题:

  • 客户端就无法向主库进行发送写命令的请求。
  • 从库没有相对应的主库进行数据同步。

因此在 Redis中使用了 哨兵机制 实现了主从库自动切换的关键机制。

哨兵机制的基本流程

哨兵是一个运行在特殊模式下的Redis进程,主从库运行的状态下,哨兵也在运行。

哨兵主要负责三个任务:监控, 选主(选择主库),通知

  • 监控:哨兵进程在运行期间,周期性地给所有主从库发送PING命令,检测他们是否在线,若从库没有相应哨兵的PING命令(回复PONG命令),则哨兵就会将其从库直接置为”下线状态”。若是主库没响应哨兵(哨兵集群模式下需要投票机制),哨兵则将主库下线,然后开始 自动切换主库 的流程。

  • 选主:哨兵会以 “筛选 + 打分” 的模式选择最适合的从库升级为主库。(下面会讲)
  • 通知:哨兵会将新主库的信息发送给所有从库,让他们执行 replcaof 命令与新主库进行连接,并且同步数据。同时哨兵还会吧新主库的信息发送给客户端,让客户端把请求都发送到新主库上。

监控:主观下线 & 客观下线

主观下线

哨兵进程会发送PING命令检测自己与主、从库之间的网络连接情况,用来判断实例的状态。如果哨兵发现主库或者从库对PING命令的响应超时了,哨兵会把他们标记为”主观下线”。

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

那如果哨兵误判了,其实主库并没有故障,一旦启动了主从切换,后续的选主和通知操作都会带来额外的计算和通信开销。

客观下线

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

只有当多个哨兵都认为主库”主观下线”了,主库才会被判定为 “客观下线”

具体判断公式:有N个哨兵时,当有 N/2 + 1个(quorum配置值) 哨兵都判断主库为 “主观下线”时,主库才会被判定为”客观下线”。

如何选取主库?

一般来说,哨兵选择从库升级为主库的过程可以分为:筛选 + 打分

筛选

首先肯定要先保证所选的从库都正常在线。不过,在选主时从库正常在线,这只能表示从库的现状良好,并不代表它就是最适合做主库的

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

具体判断流程:

Redis有一个配置项是 down-after-milliseconds:表示主从库断连的最大连接超时时间。如果从库在down-after-millisecond内都没与主库通过网络发生联系,则代表该从库断连了。如果在选主之前,该从库发生断连次数 > 10次,则代表该从库的网络不太好,不适合当主库。

打分

哨兵给筛选出来的从库进行打分可以分为三个步骤:优先级 -> 复制进度 -> 从库run ID

第一轮:优先级更高的从库优先当选

Redis哨兵是一个叫 slave-priority 配置项,用户可以手动对某个性能高的从库设置高的优先级。当哨兵在选主库的时候会将优先将优先级高的从库升级为主库。

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

当从库的优先级都一样的情况下,哨兵会将会选择和旧主库同步最接近的那个从库作为主库。

在主从复制中主库会用 master_repl_offset 记录当前最新的写操作在repl_backlog_buffer 的位置,从库会用 slave_repl_offset 记录其同步位置 。

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

第三轮 runID号小的从库得分越高

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

总流程:哨兵会按照 在线状态 和 网络状态 首先筛选过滤掉部分不符合当选主库的从库,然后按 优先级,复制进度,runID 再对剩余的从库进行打分,选出得分最高的从库当选为新主库。

哨兵集群

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

这些哨兵实例既然都不知道彼此的地址,又是怎么组成集群的呢?

pub/sub 发布 订阅 机制

哨兵间相互发现

在Redis哨兵集群中,哨兵只要与主库建立连接,就可以在主库上发布消息(pub),比如发布自己的连接信息(IP + 端口),同时也可以在主库中订阅消息(sub),比如获取其他哨兵发布的连接信息。

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

哨兵发现从库

哨兵会给主库发送 INFO 命令,主库接收到这个命令后,会返回其从库列表的信息,然后哨兵拿着这些从库的信息(IP + 端口等)与从库建立连接,并对其进行监控。

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

从本质上说,哨兵是一个运行在特定模式下的Redis进程,只不过他不提供服务请求操作,只是完成监控、选主和通知的任务

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

具体主从切换事件对应的频道如下图:

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

Redis哨兵集群中由哪个哨兵执行主从切换的过程,其实与主库”客观下线”的判断过程类似,也是一个“投票仲裁”的过程。

首先我们看看 “客观下线” 的过程:

任何一个实例只要自身判断主库“主观下线”后,就会给其他实例发送 is-master-down-by-addr 命令。接着,其他实例会根据自己和主库的连接情况,做出 Y 或 N 的响应,Y 相当于赞成票,N 相当于反对票。当一个哨兵获得了仲裁所需的赞成票数后,就可以标记主库为“客观下线”。这个所需的赞成票数是通过哨兵配置文件中的 quorum 配置项设定的。

选取哨兵执行主从切换的投票过程叫 Leader选举

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

  • 第一,拿到半数以上的赞成票;
  • 第二,拿到的票数同时还需要大于等于哨兵配置文件中的 quorum 值。

画一张图片,展示一下 3 个哨兵、quorum 为 2 的选举过程。

在 T1 时刻,S1 判断主库为“客观下线”,它想成为 Leader,就先给自己投一张赞成票,然后分别向 S2 和 S3 发送命令,表示要成为 Leader。

在 T2 时刻,S3 判断主库为“客观下线”,它也想成为 Leader,所以也先给自己投一张赞成票,再分别向 S1 和 S2 发送命令,表示要成为 Leader。

在 T3 时刻,S1 收到了 S3 的 Leader 投票请求。因为 S1 已经给自己投了一票 Y,所以它不能再给其他哨兵投赞成票了,所以 S1 回复 N 表示不同意。同时,S2 收到了 T2 时 S3 发送的 Leader 投票请求。因为 S2 之前没有投过票,它会给第一个向它发送投票请求的哨兵回复 Y,给后续再发送投票请求的哨兵回复 N,所以,在 T3 时,S2 回复 S3,同意 S3 成为 Leader。

在 T4 时刻,S2 才收到 T1 时 S1 发送的投票命令。因为 S2 已经在 T3 时同意了 S3 的投票请求,此时,S2 给 S1 回复 N,表示不同意 S1 成为 Leader。发生这种情况,是因为 S3 和 S2 之间的网络传输正常,而 S1 和 S2 之间的网络传输可能正好拥塞了,导致投票请求传输慢了。

最后,在 T5 时刻,S1 得到的票数是来自它自己的一票 Y 和来自 S2 的一票 N。而 S3 除了自己的赞成票 Y 以外,还收到了来自 S2 的一票 Y。此时,S3 不仅获得了半数以上的 Leader 赞成票,也达到预设的 quorum 值(quorum 为 2),所以它最终成为了 Leader。接着,S3 会开始执行选主操作,而且在选定新主库后,会给其他从库和客户端通知新主库的信息。

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

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

Redis切片集群