【Redis学习】4.高可用之Sentinel


1 前言

上一章,主要讲述了高可用基础方案-主从复制。读者可能会有这样一种疑问:若主节点挂掉了,谁来处理写操作啊?这个问题显而易见,单节点问题马上显现,因而需要一种机制,能够自动进行故障恢复,而不是需要人工去手动恢复,要是这样,运维估计要骂街了。

主从复制主节点宕机

Redis提供了几种实现高可用方案的架构,比如本章要介绍的哨兵模式。该模式下,通过监控主节点的状态,可以在主节点宕机后实现自动故障恢复,通过选举实现主备切换。

Redis实现高可用相关的技术。它们包括:持久化复制哨兵集群,其主要作用和解决的问题是:

  • 持久化:持久化是最简单的高可用方法(有时甚至不被归为高可用的手段),主要作用是数据备份,即将数据存储在硬盘,保证数据不会因进程退出而丢失。
  • 复制:复制是高可用Redis的基础,哨兵和集群都是在复制基础上实现高可用的。复制主要实现了数据的多机备份,以及对于读操作的负载均衡和简单的故障恢复。缺陷故障恢复无法自动化写操作无法负载均衡存储能力受到单机的限制
  • 哨兵:在复制的基础上,哨兵实现了自动化的故障恢复。缺陷写操作无法负载均衡存储能力受到单机的限制
  • 集群:通过集群,Redis解决写操作无法负载均衡,以及存储能力受到单机限制的问题,实现了较为完善的高可用方案。

2 机制

Redis Sentinel,即Redis哨兵,在Redis 2.8版本开始引入。哨兵的核心功能主节点的自动故障转移。下面是Redis官方文档对于哨兵功能的描述:

  • 监控(Monitoring):哨兵会不断地检查主节点和从节点是否运作正常。
  • 自动故障转移(Automatic failover):当主节点不能正常工作时,哨兵会开始自动故障转移操作,它会将失效主节点的其中一个从节点升级为新的主节点,并让其他从节点改为复制新的主节点。
  • 配置提供者(Configuration provider):客户端在初始化时,通过连接哨兵来获得当前Redis服务的主节点地址。
  • 通知(Notification):哨兵可以将故障转移的结果发送给客户端。

其中,监控自动故障转移功能,使得哨兵可以及时发现主节点故障并完成转移;而配置提供者通知功能,则需要在与客户端的交互中才能体现。

因而,对于哨兵模式下的Sentinel节点,其主要作用有三个:监控、选主、通知。

Sentinel节点,即哨兵节点,本身也是一种Redis节点,最大不同就是不存储数据,工作职能就是监控主节点,实际上,每个Sentinel节点会对Redis数据节点其余Sentinel节点进行监控。

一个典型的哨兵集群监控的逻辑图如下所示:

哨兵模式经典架构

3 Sentinel集群组建

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

Sentinel节点启动命令:

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

其中:

  • master-name:表示Redis主节点名称;
  • ip:主节点的 IP 地址;
  • redis-port:主节点的端口号;
  • quorum:法定投票数,即Sentinel主节点需达到quorum才有资格选举为Sentinel主节点,还有就是需达到此投票数,主节点才会判定为客观下线

另外,在Redis主节点中,有一个公共频道__sentinel__:hello,不同哨兵就是通过它来相互发现,实现互相通信的。

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

Sentinel集群发现

4 三个定时任务

本小节,我将从三个定时任务来说明Sentinel模式下,Sentinel节点如何对Redis进行监控、选主、通知,实现自动故障转移。

4.1 10秒(SMS)

这里,为了方便记忆,我将这个每隔10秒执行的定时任务命名为:SMS,意思为Sentinel-Master-Slave

通过向主节点发送info replication命令,Sentinel节点可以获取到所有从节点信息列表;接着,Sentinel节点就可以根据从节点列表中的连接信息,和每个从节点建立连接,并在这个连接上持续地对从节点进行监控。Sentinel ASentinel C可以通过相同的方法和从节点建立连接。

"SMS" 每隔10秒的定时任务

这也是为什么Sentinel节点不需要显式配置监控从节点的原因。

这个定时任务的作用具体可以表现在三个方面

  • 通过向主节点执行info命令,获取从节点的信息,这也是为什么Sentinel节点不需要显式配置监控从节点。
  • 当有新的从节点加入时都可以立刻感知出来。
  • 节点不可达或者故障转移后,可以通过info命令实时更新节点拓扑信息。

4.2 2秒(SM)

SM意思是:Sentinel-Master,即每个Sentinel节点会向Redis数据节点的__sentinel__:hello频道上发送该Sentinel节点对于主节点的判断以及当前Sentinel节点的信息,同时每个Sentinel节点也会订阅该频道,来了解其他Sentinel节点以及它们对主节点的判断。

"SM" 每隔2秒的定时任务

这个定时任务可以完成以下两个工作

  • 发现新的Sentinel节点:通过订阅主节点的__sentinel__:hello了解其他的Sentinel节点信息,如果是新加入的Sentinel节点,将该Sentinel节点信息保存起来,并与该Sentinel节点创建连接。
  • Sentinel节点之间交换主节点的状态,作为后面客观下线以及Leader选举的依据。

4.3 1秒(S-ALL)

S-ALL意思是:Sentinel节点与所有节点,包括其他Sentinel节点Redis主从节点的定时任务,即心跳机制。

每隔1秒,每个Sentinel节点会向主节点从节点其余Sentinel节点发送一条ping命令做一次心跳检测,来确认这些节点当前是否可达。如图所示。

"S-ALL" 每隔1秒的定时任务

通过上面的定时任务,Sentinel节点对主节点、从节点、其余Sentinel节点都建立起连接,实现了对每个节点的监控,这个定时任务是节点失败判定的重要依据。

ping命令的回复可以分为以下两种情况:

  • 有效回复:返回+PONG-LOADING-MASTERDOWN三种回复的其中一种;
  • 无效回复:返回除上述三种回复之外的其他回复,或者在指定时限内(down-after-milliseconds)没有返回任何回复。

4.4 选举

4.4.1 下线

下线有两种类型:

  • 主观下线:即Sentinel节点对于发送出去的心跳,超过down-after-milliseconds没有进行有效回复,Sentinel节点就会对该节点做失败判定,主观判定Redis节点下线。
  • 客观下线:如果主观判定下线的节点为主节点,则该Sentinel节点会向其他Sentinel节点发送sentinel is-master-down-by-addr命令,询问它们对该主节点的判断结果,当超过quorum个数,Sentinel节点认为主节点确实有问题,这时该Sentinel节点会做出客观下线的决定。

从节点Sentinel节点主观下线后,没有后续的故障转移操作。

这里有必要对sentinel is-master-down-by-addr命令做一个介绍,它的使用方法如下:

sentinel is-master-down-by-addr <ip> <port> <current_epoch> <runid>

其中:

  • ip:Redis主节点IP。
  • port:Redis主节点端口。
  • current_epoch:当前配置纪元。
  • runid:此参数有两种类型,不同类型决定了此API作用的不同:
    • runid等于“*”时,作用是Sentinel节点直接交换对主节点下线的判定
    • runid等于当前Sentinel节点的runid时,作用是当前Sentinel节点希望目标Sentinel节点同意自己成为领导者(Leader)的请求,有关Sentinel Leader选举,后面会进行介绍。

返回结果包含三个参数,如下所示:

  • down_state:目标Sentinel节点对于主节点的下线判断,1是下线0是在线
  • leader_runid:当leader_runid等于“*”时,代表返回结果是用来做主节点是否不可达;当leader_runid等于具体的runid,代表目标节点同意runid成为Leader。
  • leader_epoch:领导者纪元。

4.4.1.1 主观下线

示意图如下所示:

主观下线

主要通过4.3节的S-ALL定时任务,倘若心跳发出去后,在down-after-milliseconds(单位:毫秒)时间内没有收到目标节点的回复,则该Sentinel节点会对目标节点做出主观下线的判定。

4.4.1.2 客观下线

如果Sentinel节点对目标节点是Redis主节点的判定是主观下线,则该Sentinel会通过sentinel is-master-down-by-addr命令,向其他Sentinel节点发出询问,若其他Sentinel节点对于目标主节点判定主观下线的投票数超过quorum,则该节点就会判定为客观下线

这里再次强调下:是对Redis主节点判定是主观下线,才会有这步动作,如果目标节点是Sentinel节点或者是Redis从节点,则不会有此步问询动作,更不会有故障转移动作。

简而述之,客观下线只适用于主节点

客观下线

当这个Sentinel节点的赞同票数达到Sentinel节点配置文件中的 quorum 配置项设定的值后,这时主节点就会被该Sentinel节点标记为「客观下线」

4.4.2 选举

判断完Redis主节点下线后,故障转移的工作只需要一个Sentinel节点来完成即可,由哪个Sentinel节点来执行主从切换,进而完成故障转移呢?这里就需要Sentinel集群选举机制了。

Sentinel节点要想成为Leader,必须满足以下三个条件

  • 成为候选人,所谓的候选人就是想当 Leader 的Sentinel节点;
  • 拿到半数以上的赞成票;
  • 拿到的票数同时还需要大于等于Sentinel配置文件中的 quorum 值。

先说第一个条件,成为候选人。 一句话总结:

哪个Sentinel节点判断Redis主节点为「客观下线」,这个Sentinel节点就是候选人。

第二、三个条件,当Sentinel节点对目标主节点判定为主观下线后,会向其他Sentinel节点发送命令:

sentinel is-master-down-by-addr <目标主节点ip> <目标主节点port> <current_epoch> <当前Sentinel节点的runid>

,其中包含当前Sentinel节点的runid,其目的就想让其他Sentinel节点选举自己为Leader。

当其他Sentinel节点收到后,如果没有同意过其他Sentinel节点的sentinel is-master-down-by-addr命令,将同意该请求,否则拒绝。这样,就会出现两种结果:

  • 如果该Sentinel节点发现自己的票数已经大于等于max(quorum,num(sentinels)/2+1),那么它将成为Leader;
  • 否则,进行下一次选举。

以 3 个Sentinel节点为例,假设此时的 quorum 设置为 2,那么,任何一个想成为 Leader 的Sentinel节点只要拿到 2 张赞成票,就可以了。

选举使用的算法是Raft算法.

Raft算法的基本思路是先到先得:即在一轮选举中,哨兵A向B发送成为Leader的申请,如果B没有同意过其他哨兵,则会同意A成为Leader。

选举的具体过程这里不做详细描述,一般来说,哨兵选择的过程很快,谁先完成客观下线,一般就能成为Leader

这里有个很容易混淆的过程:即判定客观下线是否能够主从切换(选举制度),均涉及投票。

再举一个例子。Redis 1主4从,5个Sentinel节点部署,Sentinel配置quorum=2,如果3个Sentinel故障,当主节点宕机时,Sentinel能否判断主库“客观下线”?能否自动切换?

经过实际测试:
1、结论:Sentinel集群可以判定主库“客观下线”。
由于quorum=2,所以当一个Sentinel判断主库“主观下线”后,询问另外一个Sentinel后也会得到同样的结果,2个Sentinel都判定“主观下线”,达到了quorum的值,因此,Sentinel集群可以判定主库为“客观下线”。

2、但Sentinel不能完成主从切换。
Sentinel标记主库“客观下线”后,在选举“Sentinel领导者”时,一个Sentinel必须拿到超过多数的选票,即5/2+1=3票)。但目前只有2个Sentinel活着,无论怎么投票,一个Sentinel最多只能拿到2票,永远无法达到N/2+1选票的结果。

所以,quorum 的值建议设置为N/2+1,其中N为Sentinel节点个数,例如 3 个Sentinel节点就设置 2,5 个Sentinel节点设置为 3,而且Sentinel节点的数量N应该是奇数

4.4.3 主从切换(故障转移)

完成上述的Sentinel Leader选举后,接下来,Sentinel Leader就需要进行Redis主节点切换了,选取主节点需要从现有的从节点中进行,主从故障转移操作包含以下四个步骤

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

其中,过滤过程主要是过滤掉这些从节点:

  • “不健康”(主观下线、断线);
  • 5秒内没有回复过Sentinel节点ping响应;
  • 与主节点失联超过down-after-milliseconds*10秒。
故障转移

转移之后,整个节点逻辑图如下:

故障转移之后

5 总结

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

Sentinel一般是以集群的方式部署,至少需要 3 个Sentinel节点,Sentinel集群主要负责三件事情:监控选主通知

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

5.1 第一轮投票:判断主节点下线

当Sentinel集群中的某个Sentinel节点判定主节点下线(主观下线)后,就会向其他Sentinel节点发起命令,其他Sentinel节点收到这个命令后,就会根据自身和主节点的网络状况,做出赞成投票或者拒绝投票的响应。

当这个Sentinel节点的赞同票数达到Sentinel节点配置文件中的 quorum 配置项设定的值后,这时主节点就会被该Sentinel节点标记为「客观下线」

5.2 第二轮投票:选出Sentinel Leader

某个Sentinel节点判定主节点客观下线后,该Sentinel节点就会发起投票,告诉其他Sentinel节点,它想成为 leader。要想成为 leader 的Sentinel节点,要满足三个条件

  • 成为候选人;
  • 拿到半数以上的赞成票;
  • 拿到的票数同时还需要大于等于Sentinel节点配置文件中的 quorum 值。

5.3 由Sentinel leader 进行主从故障转移

选举出了Sentinel leader 后,就可以进行主从故障转移的过程了。该操作包含以下四个步骤

  • 第一步:在已下线主节点(旧主节点)属下的所有「从节点」里面,挑选出一个从节点,并将其转换为主节点,选择的规则:
    • 过滤掉已经离线的从节点;
    • 过滤掉历史网络连接状态不好的从节点;
    • 将剩下的从节点,进行三轮考察:优先级复制进度offsetrunId 。在每一轮考察过程中,如果找到了一个胜出的从节点,就将其作为新主节点。
  • 第二步:让已下线主节点属下的所有「从节点」修改复制目标,修改为复制「新主节点」
  • 第三步:将新主节点的 IP 地址信息,通过「发布者/订阅者机制」通知给客户端;
  • 第四步:继续监视旧主节点,当这个旧主节点重新上线时,将它设置为新主节点的从节点

6 参考


文章作者: Kezade
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Kezade !
评论
  目录