Redis Sentinel

Last Modified: 2022/11/29

概述

在不使用 cluster 情况下,Redis 哨兵机制提供了 Redis 的高可用架构。哨兵提供了以下特性:

  • Redis 实例监控;
  • 故障通知,当 Redis 实例发生故障,哨兵可以通过 api 通知系统管理员或监控程序;
  • 自动故障转移,当 master 不幸挂掉的时候,哨兵可以自动启动故障转移,将某个副本提升为新的 master,其他副本则被配置使用该新的 master,使用 Redis 的应用在连接的时候也会被通知使用的新的地址;
  • 配置提供者,客户端连接哨兵询问某个服务的 master 的地址,如果发生了故障转移,哨兵会报告新的地址;

注1:Sentinel 和 “哨兵”是一个意思,以下会混用。
注2:以下文档中有时会用“挂了”这个词代替 down,但是“挂了”这个词不够准确,“挂了”给人的感觉是服务进程停止了,但是实际上有可能是发生了网络分区,实例并没有挂,只是无法访问。

运行 sentinel

#以下两种运行方式相同,不论哪种方式都必须提供 sentinel 配置文件,配置文件用来提供初始配置,同时用于保存当前状态,例如生故障转移之后,新的 master 会被写入配置文件。
redis-sentinel /path/to/sentinel.conf
redis-server /path/to/sentinel.conf --sentinel

Sentinels 默认使用 26379 tcp 端口监听连接,因此 26379 端口必须开放以便接受来自其他 Sentinel 实例的连接,否则 Sentinel 实例无法通信,意味着无法达成一致,从而无法执行故障转移。

部署 sentinel 须知

  • 应该至少有三个 sentinel 实例才能保证可靠性;
  • 三个 sentinel 实例应该是分布在不同的机器或者虚拟机上,应该确保他们不会同时挂掉,如果都部署在一台机器上,这台机器挂了,那么整个集群显然就挂了。
  • sentinel+Redis 分布式系统不能保证在故障转移过程中的已确认的写不会丢失,因为 Redis 使用异步复制。可以想象下有两种情况会丢失数据,情况1:master 突然挂掉,写入的 master 的东西还没来得及同步到副本;情况2:master 和某个客户端(C)被分区了,但是 C 在故障转移过程中依然可以向该 master 写入数据,但是故障转移结束后,当分区恢复,master 会被 sentinel 配置为副本,并从新的 master 同步数据,因此 C 写入的数据会丢失。
  • 客户端需要对 sentinel 提供支持,大部分流行的客户端都支持 sentinel,但并不是所有客户端;
  • 高可用部署必须要在实际环境中进行测试,不测试,很可能无法正常工作,等到故障发生时,一切都已经晚了;

在第二种分区的情况下,我们可以采用以下配置,来减少数据丢失:

为什么是减少而不是杜绝?因为在 min-replicas-max-lag 时间范围内,分区中的 master 依然接受写入。

# 配置 master 至少有多少个副本能够写才接收写入
min-replicas-to-write 1
# 配置 master 在 min-replicas-to-write 条件不满足达到多长时间停止接收写入
min-replicas-max-lag 10

因为副本是异步复制,因此副本不可写入的实际含义是:副本和 master 断开连接或者副本没有向 master 发送异步确认(asynchronous acknowledges)。

配置 sentinel

sentinel 支持同时监控多组 Redis 实例,每组实例都包含一个 master 和 若干个副本。配置的时候我们只要指定 master 就可完成对组的监控。以下是典型配置:

# 监控一个组,给组取名 mymaster,组中的 master 节点的 ip 和端口分别是 127.0.0.1 和 6379,最后一个数字 2 表示法定人数。
# 这里的 2 表示当有 2 个 sentinel 都认为该 master 挂了,那么可客观认为该 master 确实挂了,并在半数以上 sentinel 投票授权许可的情况下,才真正开始故障转移。
# 可见法定人数只是用于检测 master 是否正常,数值越小,检测越灵敏,真正开启故障转移还需要得到半数以上的 sentinel 投票许可。
sentinel monitor mymaster 127.0.0.1 6379 2
# 配置多长时间内 Redis 实例不响应 ping 或者回应了错误(replying with error),sentinel 才认为实例挂了(DOWN)。
sentinel down-after-milliseconds mymaster 60000
# 这个配置是防止多个 sentinel 同时对一个 master 执行故障转移,一个 sentinel 执行后,其他的必须等 2 * failover-timeout 后才能开始。
sentinel failover-timeout mymaster 180000
# 配置多少个副本在故障转移的同时可被重新配置使用新 master,数值越大,故障转移所需时间越短。 但有时候你可能希望在故障转移过程中,副本仍然可以提供旧数据服务,那么可以将该数值配小点。
sentinel parallel-syncs mymaster 1

一个简单的配置拓扑如下,其中 M 表示 master,R 表示副本, S 表示 sentinel 实例,字母后面的数字用于区分不同的实例。 每个方框代表一台机器,quorum 是法定人数。

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

Configuration: quorum = 2

sentinel、docker 和 NAT

sentinel 依赖 pub/sub 机制自动发现其他 sentinel 实例,如果使用 docker 端口转发,那么 sentinel 收到的其他 sentinel 发布的端口和 ip 可能是不正确的。此时我们明确配置各个 sentinel 的实际 ip 和端口。

sentinel announce-ip <ip>
sentinel announce-port <port>

主机名称

从 redis 6.2 之后,开始支持使用主机名称配置 sentinel,默认不开启,具体配置方式参考这里

连 sentinel 查信息

可以连接 sentinel 查 master/replica/sentinel 的信息,可以借助于 redis-cli 连接 sentinel。

$ redis-cli -p 5000
127.0.0.1:5000> sentinel master mymaster
127.0.0.1:5000> SENTINEL replicas mymaster
127.0.0.1:5000> SENTINEL sentinels mymaster

获取当前 master

127.0.0.1:5000> SENTINEL get-master-addr-by-name mymaster
1) "127.0.0.1"
2) "6379"

测试故障转移

redis-cli -p 6379 DEBUG sleep 30

可以借助 debug 命令让 master 睡 30s, sentinel 应该会检测到 master 不可用,从而开启故障转移。

Sentinel api

sentinel 提供了 api 来检测其状态,检查 master 和副本的健康状况,运行期间修改 Redis 配置,还可以选择订阅 sentinel 发布的事件。我们可以使用 redis-cli 连接 sentinel 和 sentinel 通信。sentinel 使用的是 redis 协议,因此可以使用 redis-cli 和 sentinel 通信。 api 一大堆,具体可以参考这里

运行时重新配置 Sentinel

例如,运行期间动态调整 down-after-milliseconds 和 quorum:

SENTINEL SET mymaster down-after-milliseconds 1000
SENTINEL SET mymaster quorum 5

需要注意的是,每个监听 my-master 的 sentinel 实例都需要执行同样的命令,一个 sentinel 上执行的修改操作是不会分发到其他 sentinel 实例的。

添加删除 Sentinel

动态添加 sentinel 实例是很简单的,因为 sentinel 会自动发现其他 sentinel 实例,添加多个时候,建议一个一个的加(建议间隔 30s),让前一个添加的实例被感知之后再添加新的实例。删除 sentinel 不会自动生效,需要执行以下命令:

# 执行下面两条中的其中一条即可
SENTINEL RESET *
SENTINEL RESET mymaster
# 最后我们可以利用下面的命令检查当前监控 master 的 sentinel 实例信息
SENTINEL MASTER mymaster

SDOWN and ODOWN 失败状态

sentinel 有两个 down 的概念,分别是 sdown(subjective down)和 odown(objective down)。sdown 是一个 sentinel 内部的状态,即当前 sentinel 主观认为 Redis 实例 down 了,啰嗦下为什么说是主观呢?因为只有自己这么认为是不够客观的,当达到 quorum 个 sentinel 实例都是认为 sdown,相当于大家达成一致,此时进入 odown 状态,即可客观认为 master down 了。 odown 状态仅适用于 master,不适用于副本。

一个进入 sdown 状态的 sentinel 可以通过 SENTINEL is-master-down-by-addr 命令获取其他 sentinel 的反馈。sentinel 发出的 Ping,如果在一定时间内(由down-after-milliseconds配置)没有有效响应,则进入 sdown 状态。有效的响应包括:

  • PING replied with +PONG.
  • PING replied with -LOADING error.
  • PING replied with -MASTERDOWN error.

Sentinel 自动发现

每个 sentinel 实例都监听它所监控的 master 及副本中名为 sentinel:hello 的 channel 以感知其他 sentinel 实例,同时向该 channel 发布(通过 hello 消息)自身的 ip、端口和 runid。这样监控同一个 master 的 sentinel 可以相互感知。

有问题吗?点此反馈!

温馨提示:反馈需要登录