为什么要有哨兵

Redis 哨兵主要负责:

  • 集群健康状态监测
  • 集群故障转移
  • 通知

可以说,Redis 的高可用离不开哨兵

如何判断主节点故障

主观下线

前面提到,哨兵具有集群健康状态监测的功能

每个哨兵会每隔 1s 给每个节点发送 Ping 命令,如果某个节点在规定时间内(down-after-milliseconds 参数决定)没有响应,那么这个节点会被认为下线了

这个就叫做主观下线:哨兵节点 主观认为 节点下线

客观下线

单个哨兵认为节点下线,这个结果的可信度较低,不能就直接认为某个节点下线了

当某一个哨兵认为某个节点下线,会给其它哨兵发一条消息,看看其他哨兵的意见:

  • 如果超过 quonum 个哨兵认为该节点下线,那么哨兵集群就认为这个节点 客观下线

quonum 的值建议设为 n / 2 + 1,n 为哨兵的总个数

故障转移

由哪个哨兵进行故障转移

如果故障的是主节点,那么就要进行故障转移

那么该由哪个哨兵进行故障转移呢?

如果一个哨兵「主动」发现节点下线,那这个哨兵就是一个「候选者」

可能有多个哨兵都「主动」发现节点下线了,那么就会有多个候选者

每个候选者会给哨兵集群中的每一个哨兵发一个 leader 请求,表明自己想成为 leader,负责这一次故障转移:

  • 每个哨兵仅有一票
  • 候选者会自己给自己投一票
  • 其他哨兵,只要收到一个 leader 请求,就给这个候选者投票

如果一个候选者的得票数 超过半数(n / 2 + 1),并且 大于等于哨兵配置文件中的 quorum 值,那么认为这个候选者就是 leader

经过上面哨兵内部的选举过程,成功选出了一个 leader,接下来,就由这个 leader 来负责故障转移的过程

过程

选择新的主节点

首先要排除网络不佳的节点

前面提到了一个 down-after-milliseconds 参数,如果一个节点在 down-after-milliseconds 内没有响应哨兵的请求,那么会被认为主观下线

主观下线的次数会由哨兵集群维护,如果一个节点主观下线的次数超过了 10 次,那么认为这个节点的网络不佳

排出了网络不佳的节点后,在剩余从节点中选择出新的主节点,选择的依据如下:

  • 节点优先级(slave-priority,由用户配置):slave-priority 越低,优先级越高
  • 同步偏移 offset:如果优先级相同,那么比较 offset,同步的数据越多,优先级越高(丢失的数据越少)
  • server_id:如果节点优先级和 offset 都相同,server_id 越小,优先级越高

选择出新的主节点后,哨兵会给这个从节点发送一个 SLAVEOF no one 命令,让其独立成一个主节点

发送完 SLAVEOF no one 命令后,哨兵会监控这个节点的状态(每秒一次,INFO 命令),直到这个节点的状态为 master

将从节点指向新的主节点

当从节点成功升级为新的主节点后,哨兵会给其它从节点发送 SLAVEOF <new_ip> <new_port>,以让其它从节点修改自己的 master 为新的主节点

通知客户端主节点发生更改

现在,故障转移基本完成,只需要通知客户端最新的主节点是谁就可以了

怎么通知?

客户端与哨兵之间通过「发布-订阅」机制来实现通信

客户端会订阅 +switch-master 频道,当故障转移完毕后,哨兵会向这个频道发送新的主节点的 IP 和 Port,这样客户端就能知道新的主节点的信息了

监视原来的主节点的上线情况

哨兵还需要监视原来的主节点是否上线,如果上线,需要发送一个 SLAVEOF <new_ip> <new_port>,将其降级为从节点

为什么要部署多个哨兵节点,一个不行吗?

前面提到:单纯一个哨兵主观认为一个节点下线,这个是不可信的

因为完全有可能是哨兵自己出现了问题,而非主节点出问题

因此,为了保证可靠性,部署的是多个哨兵节点组成集群(至少大于等于 3 个)

此外,只部署一个哨兵节点,如果哨兵都挂了,整个集群都没有“领导者”了,成了一盘散沙,故障转移无法实现

哨兵集群的组成

哨兵间如何通信

在哨兵的配置文件中,并没有配置其它哨兵的 IP 和 Port,那哨兵是如何发现彼此的呢?

还是依靠「发布-订阅」机制

在 Redis 的集群中,会有一个频道 __sentinel__:hello,每个哨兵都会订阅这个频道:

  • 当一个哨兵上线,会向这个频道发送自己的 IP 和 Port
  • 其它哨兵订阅这个频道,就可以读取到这个哨兵的 IP 和 Port,进而建立通信

image

图片来自小林 Coding

哨兵如何获取其它子节点的信息

哨兵会定期向主节点发送 INFO 信息,获取子节点列表,这样,哨兵就可以知道其它子节点的 IP 和 Port 了

image

图片来自小林 Coding