参考

码哥字节:https://xie.infoq.cn/article/1c714709d00b2b55e8416fb99

小林coding: https://xiaolincoding.com/redis/cluster/sentinel.html

锦鱼不忘旧时晨:https://blog.csdn.net/weixin_44324174/article/details/108939199?spm=1001.2101.3001.6650.6&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-6-108939199-blog-81585776.235%5Ev38%5Epc_relevant_sort_base2&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-6-108939199-blog-81585776.235%5Ev38%5Epc_relevant_sort_base2&utm_relevant_index=13

为什么要有哨兵?

在 Redis 的主从架构中,由于主从模式是读写分离的,如果主节点(master)挂了,那么将没有主节点来服务客户端的写操作请求,也没有主节点给从节点(slave)进行数据同步了。

这时如果要恢复服务的话,需要人工介入:

  1. 选择一个「从节点」切换为「主节点」
  2. 让其他从节点指向新的主节点
  3. 通知上游那些连接 Redis 主节点的客户端,将其配置中的主节点 IP 地址更新为「新主节点」的 IP 地址。

这样也不太“智能”了,要是 有一个节点能监控「主节点」的状态,当发现主节点挂了 ,它自动将一个「从节点」切换为「主节点」的话,那么可以节省我们很多事情啊!

Redis 在 2.8 版本以后提供的 哨兵(Sentinel)机制,它的作用是实现 主从节点故障转移。它会监测主节点是否存活,如果发现主节点挂了,它就会选举一个从节点切换为主节点,并且把新主节点的相关信息通知给从节点和客户端。

哨兵节点主要负责三件事情:监控、选主、通知

如何判断主节点真的故障了?(监控)

哨兵会每隔 1 秒所有主从节点发送 PING 命令,当主从节点收到 PING 命令后,会发送一个响应命令给哨兵,这样就可以判断它们是否在正常运行。

如果主节点或者从节点没有在规定的时间内响应哨兵的 PING 命令,哨兵就会将它们标记为「主观下线」。这个「规定的时间」是配置项 down-after-milliseconds 参数设定的,单位是毫秒

那什么是客观下线

哨兵在部署的时候不会只部署一个节点,而是用多个节点部署成哨兵集群最少需要三台机器来部署哨兵集群),通过多个哨兵节点一起判断,就可以就可以避免单个哨兵因为自身网络状况不好,而误判主节点下线的情况。

当一个哨兵判断主节点为「主观下线」后,就会向其他哨兵发起 is-master-down-by-addr 命令,其他哨兵收到这个命令后,就会根据自身和主节点的网络状况,做出赞成投票或者拒绝投票的响应。

当这个哨兵的赞同票数达到哨兵配置文件中的 quorum (这里不需要过半)配置项设定的值后,这时主节点就会被该哨兵标记为「客观下线」

PS:quorum 的值一般设置为哨兵个数的二分之一加1,例如 3 个哨兵就设置 2。

哨兵判断完主节点客观下线后,哨兵就要开始在多个「从节点」中,选出一个从节点来做新主节点。

 

哨兵选主

由哪个哨兵作为 Leader 来进行主从故障转移?

哨兵的选举采用的是Raft算法,Raft是一个用户管理日志一致性的协议,它将分布式一致性问题分解为多个子问题:Leader选举、日志复制、安全性、日志压缩等。Raft将系统中的角色分为领导者(Leader)、跟从者(Follower)和候选者(Candidate).......

大致简单过程

  1. 每个做主观下线的sentinel节点像其他sentinel节点发送命令,要求将自己设置为领导者
  2. 接收到的sentinel可以同意或者拒绝
  3. 如果该sentinel节点发现自己的 (1)票数已经超过半数 并且 (2)超过了quorum
  4. 如果此过程选举出了多个领导者,那么将等待一段时重新进行选举

为什么哨兵节点至少要有 3 个?

只有 2 个哨兵节点,此时如果一个哨兵想要成功成为 Leader,必须过半,也就是说得获得 2 票,而不是 1 票。如果哨兵集群中有个哨兵挂掉了,那么就只剩一个哨兵了,如果这个哨兵想要成为 Leader,这时票数就没办法达到 2 票,就无法成功成为 Leader,这时是无法进行主从节点切换的。

因此,通常我们至少会配置 3 个哨兵节点。这时,如果哨兵集群中有个哨兵挂掉了,那么还剩下两个个哨兵,如果这个哨兵想要成为 Leader,这时还是有机会达到 2 票的,所以还是可以选举成功的,不会导致无法进行主从节点切换。

Redis 1 主 4 从,5 个哨兵 :

  • quorum 设置为 2,如果 2 个哨兵故障,当主节点宕机时,如果有 3 个哨兵故障的话。此时哨兵集群还是可以判定主节点为“客观下线”(投票可以超过 quorum),但是哨兵不能完成主从切换(但是票数不可以过半)。所以既然怎么样都不能完成主从切换的话(怎么样都不能过半),判定为客观下线相当于是做了无用功。
  • quorum 设置为 3,如果 2 个哨兵故障,当主节点宕机时,如果有 3 个哨兵故障的话。此时哨兵集群不可以判定主节点为“客观下线”,也不能完成主从切换。

所以,quorum 的值建议设置为哨兵个数的二分之一加1,例如 3 个哨兵就设置 2,5 个哨兵设置为 3,而且哨兵节点的数量应该是奇数。

 

主节点故障如何进行主从切换?

主从故障转移操作包含以下四个步骤:

  1. 第一步:在已下线主节点(旧主节点)属下的所有「从节点」里面,挑选出一个从节点,并将其转换为主节点。
  2. 第二步:让已下线主节点属下的所有「从节点」修改复制目标,修改为复制「新主节点」;
  3. 第三步:将新主节点的 IP 地址和信息,通过「发布者/订阅者机制」通知给客户端;
  4. 第四步:继续监视旧主节点,当这个旧主节点重新上线时,将它设置为新主节点的从节点;

步骤一:从故障主节点的从节点中选出新的主节点

首先过滤掉网络状况不好的从库:

  • 从库当前在线状态,下线的直接丢弃;

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

然后进行几轮评估:

  1. 有个叫 slave-priority 配置项,可以给从节点设置优先级。每一台从节点的服务器配置不一定是相同的,我们可以根据服务器性能配置来设置从节点的优先级。
  2. 如果 1 同,比较谁的复制进度更接近之前的主节点,即 某个从节点的 slave_repl_offset 最接近之前主节点的 master_repl_offset
  3. 如果 2 同,比较用来唯一标识从节点的 ID 号

在选举出从节点后,哨兵 leader 向被选中的从节点发送 SLAVEOF no one 命令,让这个从节点解除从节点的身份,将其变为新主节点。

在发送 SLAVEOF no one 命令之后,哨兵 leader 会以每秒一次的频率向被升级的从节点发送 INFO 命令(没进行故障转移之前,INFO 命令的频率是每十秒一次),并观察命令回复中的角色信息,当被升级节点的角色信息从原来的 slave 变为 master 时,哨兵 leader 就知道被选中的从节点已经顺利升级为主节点了。

步骤二:将其余从节点指向新主节点

哨兵 leader 向其余所有从节点发送 SLAVEOF ,让它们成为新主节点的从节点。

步骤三:通知客户的主节点已更换

通过 Redis 的发布者/订阅者机制来实现的。每个哨兵节点提供发布者/订阅者机制,客户端可以从哨兵订阅消息。

客户端和哨兵建立连接后,客户端 会 订阅哨兵提供的频道。主从切换完成后,哨兵就会向 +switch-master 频道 发布新主节点的 IP 地址和端口 的消息,这个时候客户端就可以收到这条信息,然后用这里面的新主节点的 IP 地址和端口进行通信了。

哨兵提供消息订阅频道有很多,不同频道包含了主从节点切换过程中的不同关键事件,几个常见的事件如下:

 

步骤四:将旧主节点变为从节点

故障转移操作最后要做的是,继续监视旧主节点,当旧主节点重新上线时,哨兵集群就会向它发送 SLAVEOF 命令,让它成为新主节点的从节点.

 

 

哨兵集群是如何组成的?

在配置哨兵的信息时,只需要填下面这几个参数,设置主节点名字、主节点的 IP 地址和端口号以及 quorum 值。

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

不需要填其他哨兵节点的信息,我就好奇它们是如何感知对方的,又是如何组成哨兵集群的?

后面才了解到,哨兵节点之间是通过 Redis 的发布者/订阅者机制相互发现的。

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

在下图中,哨兵 A 把自己的 IP 地址和端口的信息发布到__sentinel__:hello 频道上,哨兵 B 和 C 订阅了该频道。那么此时,哨兵 B 和 C 就可以从这个频道直接获取哨兵 A 的 IP 地址和端口号。然后,哨兵 B、C 可以和哨兵 A 建立网络连接。

通过这个方式,哨兵 B 和 C 也可以建立网络连接,这样一来,哨兵集群就形成了。

 

 

哨兵集群会对「从节点」的运行状态进行监控,那哨兵集群如何知道「从节点」的信息?

主节点知道所有「从节点」的信息,所以哨兵会每 10 秒一次的频率向主节点发送 INFO 命令获取所有「从节点」的信息。

如下图所示,哨兵 B 给主节点发送 INFO 命令,主节点接受到这个命令后,就会把从节点列表返回给哨兵。接着,哨兵就可以根据从节点列表中的连接信息,和每个从节点建立连接,并在这个连接上持续地对从节点进行监控。哨兵 A 和 C 可以通过相同的方法和从节点建立连接。

正式通过 Redis 的发布者/订阅者机制,(1)哨兵之间可以相互感知,然后组成集群。同时,(2)哨兵又通过 INFO 命令,在主节点里获得了所有从节点连接信息,于是就能和从节点建立连接,并进行监控了。

 

总结

哨兵主要任务

Redis 哨兵机制是实现 Redis 不间断服务的高可用手段之一。主从架构集群的数据同步,是数据可靠的基础保障;主库宕机,自动执行主从切换是服务不间断的关键支撑。

Redis 哨兵机制实现了 master 宕机时主从库的自动切换:

  • 监控 master 与 slave 运行状态,判断是否客观下线

  • master 客观下线后,选择一个 slave 切换成 master;

  • 通知 其余slave 和客户端  master 信息。

哨兵集群原理

为了避免单个哨兵故障后无法进行主从切换,以及为了减少误判率,又引入了哨兵集群;哨兵集群又需要有一些机制来支撑它的正常运行:

  • 基于 pub/sub 机制(哨兵订阅主节点上_sentinel__:hello频道)实现哨兵集群之间感知和通信

  • 基于 INFO 命令获取 master 上的 slave 列表,帮助 哨兵与 slave 建立连接;

  • 通过哨兵的 pub/sub(客户端订阅哨兵上 +switch-master 频道),实现了与客户端和哨兵之间的事件通知。

主从切换,并不是随意选择一个哨兵就可以执行,而是通过投票仲裁,选择一个 Leader,由这个哨兵 Leader 负责主从切换