Redis 笔记-哨兵(Sentinel)

1,567 阅读12分钟

介绍

redis sentinel 和 redis cluster 两者都是保证 redis 集群高可用的手段。哨兵在复制(Replication)基础上实现了 自动化的故障恢复 功能,解决了当发生故障时,需要人工参与进行主从切换的痛点,但是 并没有解决 redis 读写的负载均衡问题。其作用官网介绍如下:

  • 集群监控(Monitoring),负责监控 redis master 和 slave 客户端是否正常工作
  • 消息通知(Notification),如果某个 redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员
  • 故障转移(Automatic failover),如果 master node 挂掉了,会自动转移到 slave node 上
  • 配置中心,如果故障转移发生了,通知 client 客户端新的 master 地址

哨兵本身也是分布式的,作为一个哨兵集群去运行,互相协同工作,当需要发生故障转移时,判断一个 master node 是宕机了,需要大部分的哨兵都同意才行,实际发起也需要多个节点同意。

配置文件

下载的 redis 安装包中包含 sentinel.conf 的模板配置文件,如下是一个 sentinel 所需得最简配置:

bind 127.0.0.1
port 26379
daemonize yes
logfile "26379.log"

sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1

sentinel monitor resque 192.168.1.3 6380 4
sentinel down-after-milliseconds resque 10000
sentinel failover-timeout resque 180000
sentinel parallel-syncs resque 5

此时该 sentinel 监听了两个不同 master 节点,名字分别是 mymaster、resque(可自定义)。没必要精确地指出从节点(Slave),因为 sentine 支持自动发现(auto-discovered)。

当 sentinel 发现 slave 时,会将其自动更新到配置文件中。当所监听的集群发生故障转移(某个 slave 晋升为 master)和新的 sentinel 节点被发现时,也会自动更新配置文件。

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

再来看示例配置(只看上半部分,下半边同上),监听了一个名为 mymaster 的节点,它的 ip 地址是 127.0.0.1 端口是 6379,quorum 为 2。

quorum 配置指定了标记为 master 客观下线(odown)的 sentinel 节点数量。换句话说,当只有 quorum 数量的 sentinel 节点认为某个 master 节点宕机时,该 master 才算是客观下线。

该配置 只用在探测节点存活 时,为了能够做真正的故障转移工作。真正决定 是否进行故障转移还需要 majority 数量 sentinel 节点同意。注意两者的区别,下文会详细讲到。再来看下其他的配置:

  • down-after-milliseconds

该配置指定了 sentinel 节点认为 mymaster 节点宕机所需的毫秒数。如果 mymaster 节点没有在给定的毫秒数之内没返回 sentinel 发送的 PING 命令或者是返回一个错误,那么 sentinel 节点会将 mymaster 节点标记为主观下线(subjectively down,简称 SDOWN)。

  • parallel-syncs

选项指定了在执行故障转移时,最多可以有多少个从服务器同时对新的主服务器进行同步,这个数字越小,完成故障转移所需的时间就越长。slave 节点在加载从新的 master 上同步过来的文件时,会导致一段时间内不能处理命令请求,所以这个值如果设为当前 slave 节点数时,有可能导致所有从服务器在一段时间内不能处理命令请求的状态。

  • failover-timeout

配置值单位为毫秒,默认值为 3 分钟,用于以下 4 个场景:

  • 同一个 sentinel 对同一个 master 两次 failover(故障转移) 之间的间隔时间。
  • 当一个 slave 从一个错误的 master 那里同步数据开始计算时间。直到 slave 被纠正为向正确的 master 那里同步数据时。
  • 当想要取消一个正在进行的 failover 所需要的时间。
  • 当进行 failover 时,配置所有 slaves 指向新的 master 所需的最大时间。不过,即使过了这个超时,slaves 依然会被正确配置为指向 master,但是就不按parallel-syncs 所配置的规则来了。

命令行操作

  • info sentinel:获取监控的所有主节点的基本信息
  • sentinel masters:获取监控的所有主节点的详细信息
  • sentinel master mymaster:获取监控的主节点 mymaster 的详细信息
  • sentinel slaves mymaster:获取监控的主节点 mymaster 的从节点的详细信息
  • sentinel sentinels mymaster:获取监控的主节点 mymaster 的哨兵节点的详细信息
  • sentinel get-master-addr-by-name mymaster:获取监控的主节点 mymaster 的地址信息,前文已有介绍
  • sentinel is-master-down-by-addr:哨兵节点之间可以通过该命令询问主节点是否下线,从而对是否客观下线做出判断

配置热更新

需要注意一点sentinel 不会将动态修改的配置同步给其他的 sentinel 节点,也就说,想要在运行时修改 sentinel 配置,并且 sentinel 集群能够正常工作,就必须在每个节点上执行一次。有以下几个命令:

  • SENTINEL MONITOR <name> <ip> <port> <quorum>:新增监控节点
  • SENTINEL REMOVE <name>:移除已监控节点
  • SENTINEL SET <name> <option> <value>:修改配置

节点增加、删除

新增哨兵节点非常简单,按照配置文件在启动一个 sentinel 节点就行。移除节点稍微麻烦,将节点进程关闭后,依次再另外的每台 sentinel 节点上执行 setninel reset *,执行后使用 sentinel sentinels <主节点名字> 查看其他的 sentinel 节点信息,之所以需要 reset 是因为

Sentinels never forget already seen Sentinels, even if they are not reachable for a long time

两种哨兵集群示范

双节点哨兵集群(错误)

为什么 sentinel 集群只有 2 个节点时无法正常工作?

哨兵集群必须部署 2 个以上节点,如果哨兵集群仅仅部署了个 2 个哨兵实例

+----+         +----+
| M1 |---------| R1 |
| S1 |         | S2 |
+----+         +----+

此时配置 quorum=1。master 宕机,s1 和 s2 中只要有 1 个哨兵认为 master 宕机就可以进行切换,同时 s1 和 s2 中会选举出一个哨兵来执行故障转移。同时这个时候,需要 majority,也就是大多数哨兵都是运行的,2 个哨兵的 majority 就是 2,只有 2 个哨兵都运行着,才可以允许执行故障转移。

但是如果整个 M1 和 S1 运行的机器宕机,那么哨兵只存在 1 个,此时就没有足够数量的 majority 来允许执行故障转移,虽然另外一台机器还有一个 R1,但是故障转移不会执行。

三节点哨兵集群(正确)

哨兵至少需要 3 个实例,来保证自己的健壮性

       +----+
       | M1 |
       | S1 |
       +----+
          |
+----+    |    +----+
| R2 |----+----| R3 |
| S2 |         | S3 |
+----+         +----+

此时配置 quorum=2。如果 M1 所在机器宕机了,那么三个哨兵还剩下 2 个,S2 和 S3 可以一致认为 master 宕机,然后选举出一个来执行故障转移。同时 3 个哨兵的 majority 是2,所以还剩下的 2 个哨兵运行着,就可以允许执行故障转移

重要概念

说功能之前,有必要知道一些重要的概念,以便于更好地理解金

1、sdown 、odown 故障状态

  • sdown 是主观下线,单个哨兵认为 master 宕机了,就叫主观下线
  • odown 是客观下线,quornum 数量的哨兵都认为 master 宕机,就叫客观下线

sdown 达成条件:某个哨兵 ping master 时,超过 is-master-down-after-milliseconds 毫秒

sdown -> odown 转换条件:单个哨兵在指定时间内收到 quorum 数量的哨兵的 master 宕机消息,就认为是 odown,客观认为 master 宕机。

2、quorum、majority 概念

每次哨兵需要做主备切换是,首先需要 quornum 数量的哨兵认为 odown,然后选择出一个哨兵节点来做切换,这个哨兵还得得到 majority 哨兵的授权才能正式进行切换。

关于 majority(大多数)这个概念,新版的 redis 官网上没有给出具体的解释,后来在老版的官网上找到了答案:

  • N mast be equal or greater to the majority of the voters (num_votres/2+1),

连带的给出了 majority 的算法,意思是过半数。

所以,在部署 sentinel 集群时,建议采用奇数个节点进行部署,当为偶数时,会出现平票情况,从而导致选举失败。显然集群节点奇数时没有这问题

如果 quorum < majority,比如 5 个哨兵,majority 为 3,quorum 设置为 2。那么只要 2 个哨兵把 master 标记为客观宕机,一次主从切换就会被触发,但是只能在 3 个哨兵节点都授权的情况真正发生切换。

结论:触发切换动作更加敏感,执行切换谨慎

如果 quornum >= majority,哨兵数量为 5,quornum 设置为 5,那么必须得 5 个哨兵节点都把 master 标记为客观宕机,才会触发 failover。

结论:触发切换动作谨慎,执行切换较容易

功能一:故障转移

  1. 故障 master 主观下线 单个 sentinal 标记 master 为 unreachable。在 down-after-milliseconds 时间内没有回复 sentinal 节点的心跳包。
  2. 故障 master 客观下线 超过 quornum 数量的 sentinal 节点认为该 master 节点主观下线。
  3. sentinal 集群 leader 选举 如果一个Sentinel节点获得的选举票数达到Leader最低票数(quorum和Sentinel节点数/2+1的最大值),则该Sentinel节点选举为Leader;否则重新进行选举。
  4. sentinal leader 进行新 master 选举,详见下文,选举算法和优先级

选举算法和优先级

如果一个 master 被标记为客观下线,此时哨兵就会进行主备切换操作,并选择一个 slave 节点为新的 master,会参考 slave 的一些信息:

  • slave 跟 master 断开连接的时长
  • slave 的优先级
  • offset (拷贝数据的大小)
  • run id 的大小

如果一个 slave 跟 master 断开连接已经超过了 down-after-milliseconds 的 10 倍,外加 master 宕机的时长,那么此 slave 就被认为不适合选举为 master。

接着会对 slave 进行排序

  1. 按照 slave 的优先级进行排序,slave priority 越低,优先级越高
  2. 优先级相同,比较 offset 大小,offset 越靠后(复制数据越多),优先级越高
  3. 如以上两项条件都相同,比较 run id 大小,小的优先

configuration epoch

执行切换的哨兵,会从新的 master(slave->master)上得到一个 configuration epoch,代表一个版本号,每次切换这个 version 版本号都是唯一的。

如果第一个选举出的哨兵切换失败了,其余的哨兵会等到 failover-timeout 时间后,继续进行切换,此时会重新获取新的 configuration epoch 作为新的 version 号。

功能二:自动发现机制

基于 redis 的 sub/pub 机制,一个 Sentinel 节点可以与多个 Sentinel 节点进行连接, 各个 Sentinel 节点之间可以互相检查对方的可用性,并进行信息交换。

哨兵会以 两秒一次的频率 向被它监视的所有主服务器和从服务器的 __sentinel__:hello 频道发送一条信息, 信息中包含了 Sentinel 的 IP 地址、端口号和运行 ID (runid)和主服务器当前配置。

同时每个 sentinel 还监听了 __sentinel__:hello 频道,并且在内部维护了一个 sentinel 节点列表,当发现新 sentinel 节点时,会将其添加进这个列表中。

若集群中某个 sentinal 收到频道的消息,并发现当前 master 配置比较旧(configuration epoch),那么这个 sentinal 会将 master 升级到最新的配置。(configuration propagation )

功能三:自动同步和纠正配置

哨兵会负责纠正 slave 的一些配置,比如 slave

  • 要成为潜在的 master 候选者,哨兵会确保 slave 在复制现有 master 的数据(发生故障转移之前)
  • 如果 slave 连接到了一个错误的 master 上,哨兵会确保他们连接到正确的 master 上(发生故障转移之后)

数据丢失问题

主备切换时存在数据丢失,因为 master 和 slave 之间的数据通信是基于 异步复制 的,如果在 master 在异步复制到 slave 之前就宕机了,存在数据丢失的情况。

同样 集群脑裂 也会导致数据丢失,某个 master 节点所在的网络 脱离了正常的集群网络,跟其他的 slave 节点失去了联系,此时 sentinal 则会 认为该 master 宕机(实际还存活),将集群中的某个 slave 节点晋升成为新的 master。此时集群中就会存在两个 master 节点,称之为 脑裂

如果此时前端 client 连接的还是旧的 master,写请求还是继续会往此 master 路由。如果后续旧 master 网络恢复,重新接入了集群,就会作为 slave 角色,再进行数据同步的话,部分数据就会丢失。

改善方案

只能减少异步复制、脑裂导致的数据丢失, 尽管不能保证写操作的持久性,但起码丢失数据的窗口会被严格限制在指定的秒数中。

min-slaves-to-write 1
min-slaves-max-lag 10

如果至少有 min-slaves-to-write 个从服务器,并且这些服务器的延迟值都少于 min-slaves-max-lag 秒,那么主服务器就会执行客户端请求的写操作。

如果条件达不到 min-slaves-to-writemin-slaves-max-lag 所指定的条件, 那么写操作就不会被执行,主服务器会向请求执行写操作的客户端返回一个错误。

参考