一个 Redis Master 宕机,两个 Replica 却都还健康。数据明明有副本,服务却不会自己恢复:Replica 不会主动接管,客户端也不会凭空找到新的写入地址。主从复制的工作到这里已经结束,Sentinel 要处理的,正是故障发生后的这段空白。
要看懂 Sentinel,可以跟着一次完整的故障转移往下走:多个 Sentinel 如何交换判断并授权其中一个执行切换,新 Master 怎样被选出,整个复制组又如何接受新拓扑。本文先划清职责边界,再沿这条路径拆解原理;第三节转向 Sentinel 本身,说明这些独立进程如何发现同伴并协调行动。随后用本地三 Redis、三 Sentinel 环境观察关键状态,最后落到参数取舍和生产排障。
如果还不熟悉主从复制,可以先读《Redis 主从复制》。本文的机制说明基于 Redis 8.10.0 和源码 commit 5279a8d44818a5ca51e9abb91a9b8ce481d3c88b;实验结果均按实际操作记录,避免把依赖运行环境的现象写成定论。
1. Sentinel 解决什么问题#
主从复制负责把 Master 的数据变化传给 Replicas。它解决的是“故障后有没有副本”,没有回答以下问题:
- 谁判断 Master 已经不可用;
- 哪个 Replica 应该接管;
- 谁修改其余 Replicas 的上游;
- 客户端到哪里查询新的写入地址。
如果这些动作依赖人工处理,服务恢复时间就取决于告警、判断和操作速度。Sentinel 把这条控制链自动化,但不改变 Redis 异步复制的性质。
这条控制链决定了 Sentinel 的职责范围。Sentinel 是独立于 Redis 数据节点的控制面:一个 Sentinel 进程可以监控多个 Master 复制组,同一个复制组通常由多个 Sentinel 共同管理。
| 职责 | Sentinel 做什么 |
|---|---|
| 监控 | 周期性检查 Master、Replicas 和其他 Sentinels 是否可达 |
| 通知 | 通过事件、日志或通知脚本报告实例状态变化 |
| 自动故障转移 | 在满足故障判断和授权条件后选择 Replica、完成晋升并重配拓扑 |
| 配置发现 | 向客户端返回当前 Master 地址,并报告已知 Replicas |
Sentinel 不在 Redis 命令的数据路径上。客户端先向 Sentinel 查询地址,随后直接连接 Redis;Sentinel 不转发读写请求,也不接管已有连接。
明确职责后,还要看它没有接手哪些工作:
| 问题 | Sentinel 是否解决 | 实际边界 |
|---|---|---|
| 强一致或零写入丢失 | 否 | Redis 仍是异步复制,已确认但尚未复制的写入可能丢失 |
| 透明代理与连接迁移 | 否 | 客户端必须实现发现、地址缓存失效和重连 |
| Replica 读流量准入 | 否 | Sentinel 能报告节点,业务仍要检查同步、延迟和健康状态 |
| 数据分片与水平扩容 | 否 | Sentinel 管理的是 Master-Replica 复制组,不是 Redis Cluster 槽位 |
| 跨地域容灾 | 否 | 故障域、延迟、数据恢复目标和流量调度需要单独设计 |
Sentinel 的目标是缩短故障发现和拓扑恢复过程。它不会把异步复制变成共识协议,也不能替客户端决定何时恢复业务流量。第 5 节再讨论如何收窄这些风险。
这些职责和边界对应到 Redis 架构中,就是下面的部署关系:
图里有两条不同的路径。数据路径是客户端与 Redis 之间的命令,以及 Master 到 Replicas 的复制流;控制路径是 Sentinel 对节点的监控、Sentinels 之间的信息交换,以及客户端向 Sentinel 发起的地址查询。即使 Sentinel 短时不可用,已经拿到地址的客户端仍可能继续访问 Redis;但它无法在下一次切换时重新发现地址。
三个 Sentinel 的意义不只是多启动两个进程。故障判断需要多个观察者交换意见,故障转移还需要多数派授权。它们若全部位于同一宿主机或同一故障域,进程数并不能抵御该故障域整体失效。
2. Sentinel 原理:监控、选主与拓扑收敛#
Sentinel 的工作主线只有三步:持续监控 Redis 复制组,故障成立后选出新 Master,再把其他节点和客户端引向新拓扑。下图先给出完整过程,后面再分别说明监控、选主和拓扑收敛。
本文实验环境用下面四项配置控制这条流程:
sentinel monitor mymaster redis-1 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 30000mymaster 是监控组名称,redis-1 6379 是初始 Master 地址,末尾的 2 是形成 ODOWN 所需的 quorum。down-after-milliseconds 控制故障判断,另外两个参数用于拓扑切换。配置里不需要逐个列出 Replicas 和其他 Sentinels,它们的发现过程放到第 3 节说明。
2.1 监控:建立视图并判断故障#
Sentinel 通常每 10 秒读取 Master 和 Replicas 的 INFO。它从 Master 的复制信息中发现 Replicas,再继续采集这些节点的角色、上游、复制偏移量和优先级。每个 Sentinel 据此维护自己的监控视图,而不是读取一份由所有 Sentinel 共享的内存状态。
完成发现后,Sentinel 通常每秒向已知 Redis 节点和 peer Sentinels 发送 PING。一个节点在 down-after-milliseconds 内持续没有可接受响应时,当前 Sentinel 将其标记为 SDOWN;对于 Master,它还会通过 SENTINEL is-master-down-by-addr 收集其他 Sentinels 的判断,达到 quorum 后形成 ODOWN。
quorum 控制的是故障判定:至少多少个 Sentinel 同意 Master 下线,才能把它从 SDOWN 推进到 ODOWN。这个值由
sentinel monitor配置,不会随 Sentinel 数量自动计算。
| 状态 | 判断范围 | 条件 |
|---|---|---|
| SDOWN | 单个 Sentinel 的本地判断 | 节点持续没有及时回应 |
| ODOWN | 仅针对 Master 的故障结论 | 至少 quorum 个 Sentinel 报告 Master 下线 |
SDOWN 和 ODOWN 都不会直接改变 Redis 角色。down-after-milliseconds 越小,检测越快,但短时丢包、宿主机暂停和 Redis 主线程阻塞也越容易触发误判。
2.2 选主:先选执行者,再选新 Master#
Sentinel 的“选主”有两层。Master 进入 ODOWN 后,Sentinels 先按故障转移纪元投票,选出负责本轮切换的 Sentinel。在自动故障转移中,候选者的得票数必须同时达到已知 Sentinel 的绝对多数和配置的 quorum,也就是 max(floor(N / 2) + 1, quorum)。N 是当前 Master 视图中已知的 Sentinel 总数,包含自身;每个 Sentinel 在同一纪元只投一票。
多数派避免网络分区中的 Sentinel 少数派自行发起切换。quorum 主要控制 ODOWN 的敏感程度,但当它高于多数派时,也会抬高执行者的授权门槛。
三个 Sentinel、quorum 为
1或2时,授权都至少需要2票。五个 Sentinel、quorum 为2时需要3票;quorum 为4时则需要4票。
这里讨论的是自动故障转移。手动执行 SENTINEL FAILOVER 属于强制切换,不需要其他 Sentinels 授权。
执行者确定后,再由 sentinelSelectSlave() 从 Replicas 中选择新的 Redis Master。下面的过滤和排序规则以 Redis 8.10.0 的 src/sentinel.c 为准。
第一环节:排除不合格的 Replica#
/* 摘自 src/sentinel.c:5105 附近,省略无关代码;中文注释为本文补充。 */
mstime_t master_down_time = 0;
if (master->flags & SRI_S_DOWN)
master_down_time = mstime() - master->s_down_since_time;
/* Replica 最长可断连时间:
* Master 已处于 SDOWN 的时长 + 10 * down-after-milliseconds。 */
mstime_t max_master_down_time =
master_down_time + master->down_after_period * 10;
if (slave->flags & (SRI_S_DOWN|SRI_O_DOWN)) continue; /* 已被判定下线 */
if (slave->link->disconnected) continue; /* Sentinel 连接已断开 */
if (mstime() - slave->link->last_avail_time >
sentinel_ping_period * 5) continue; /* 默认约 5 秒不可用 */
if (slave->slave_priority == 0) continue; /* 明确禁止晋升 */
/* Master 已进入 SDOWN 时,只接受默认 5 秒内的 INFO;
* 其他情况下接受默认 30 秒内的 INFO。 */
if (master->flags & SRI_S_DOWN)
info_validity_time = sentinel_ping_period * 5;
else
info_validity_time = sentinel_info_period * 3;
if (mstime() - slave->info_refresh > info_validity_time) continue;
if (slave->master_link_down_time > max_master_down_time) continue;Master 进入 SDOWN 后,Sentinel 会把 INFO 有效期从默认约 30 秒收紧到约 5 秒。最后一个条件则给 Replica 留出与故障持续时间相关的断连余量,而不是使用固定阈值。
过滤条件中没有 loading。Sentinel 也不会把 INFO 中的 loading 状态解析为候选字段,因此正在 loading 的 Replica 仍可能进入排序并被选中。业务侧不能据此判断节点已经完成加载。
第二环节:对剩余 Replica 排序#
/* 摘自 src/sentinel.c:5077 附近;返回值越小,排序越靠前。 */
if ((*sa)->slave_priority != (*sb)->slave_priority)
return (*sa)->slave_priority - (*sb)->slave_priority; /* priority 小者优先 */
if ((*sa)->slave_repl_offset > (*sb)->slave_repl_offset)
return -1; /* offset 大者优先 */
else if ((*sa)->slave_repl_offset < (*sb)->slave_repl_offset)
return 1;
/* priority 和 offset 都相同时,没有 run ID 的节点排在最后。 */
if (sa_runid == NULL && sb_runid == NULL) return 0;
else if (sa_runid == NULL) return 1;
else if (sb_runid == NULL) return -1;
return strcasecmp(sa_runid, sb_runid); /* run ID 字典序小者优先 */只有通过第一环节的 Replicas 才会参与排序。比较顺序是 priority、复制偏移量、run ID;priority 代表运维意图,所以它排在复制进度之前。这套规则不能消除异步复制的数据缺口,也不保证故障转移零丢失。
2.3 设置拓扑:晋升、重配与配置传播#
在自动故障转移中,Sentinels 投票选出的是本轮故障转移的执行者,不是新 Master。图中 S1 获得授权后,按 2.2 节的规则选出 Replica A。SLAVEOF 是 Redis 8.10 Sentinel 源码沿用的历史命令名,语义等同于 REPLICAOF;图中带加号的标签均为各进程独立产生的本地事件。
Redis 8.10.0 的 HELLO 是由逗号分隔的八个字段:
sentinel_ip,sentinel_port,sentinel_runid,current_epoch,
master_name,master_ip,master_port,master_config_epoch前四个字段标识发送者及其当前纪元,后四个字段描述被监控的 Master 配置。确认 Replica A 晋升后,S1 用它的地址和本轮故障转移纪元构造 HELLO。
HELLO 有两条周期传播路径:
- Redis 数据节点 Pub/Sub:通过 Master 或 Replica 的
__sentinel__:hello频道广播配置,同时发现未知的 peer Sentinel。 - 已知 peer 直连:通过 Sentinel 实现的 fake
PUBLISH点对点刷新配置;它不会真正广播,也不能发现未知 peer。
两条路径并行工作,不是互斥的主备链路。就传播时机而言,晋升确认只是把下一轮 HELLO 提前。
接收方分开处理 HELLO 中的两个纪元:较大的 current_epoch 会推进当前纪元,但只有严格更大的 master_config_epoch 才会覆盖本地 Master 配置。地址变化时产生的 +config-update-from 和 +switch-master 是本地事件,不是配置传播消息。
S1 进入 RECONF_SLAVES 后,sentinelGetCurrentMasterAddress() 已会返回 Replica A,但其内部 Master 条目要到重配阶段收尾才替换。因此,各 Sentinel 可能在短时间内返回不同地址。parallel-syncs 只限制重配并发数;该阶段可因 failover-timeout 收尾,单个 Replica 也可能因内部 slave-reconf-timeout 被按完成处理,所以故障转移完成不代表所有 Replica 都已跟随新 Master。
+switch-master 不是全局收敛信号。客户端应使旧地址缓存失效,按图中流程重新查询并用 ROLE 校验。旧 Master 恢复后,Sentinel 会尝试把它重配为当前 Master 的 Replica,不会自动 failback。
3. Sentinel 是什么:实例发现与协作#
Sentinel 是以 Sentinel 模式运行的独立 redis-server 进程,默认监听 26379,不保存业务数据。一个进程可以监控多个 Master 复制组;每个实例有自己的 myid、连接和本地视图。多个实例常被叫作“Sentinel 集群”,但它们之间没有固定 Master,也没有需要预先登记的成员列表。
sentinel monitor mymaster redis-1 6379 2 只是发现入口:它给出组名、初始 Master 地址和 quorum。Sentinel 连接 Master,通过 INFO 找到 Replicas;随后在每个已知 Redis 节点上发布并订阅 __sentinel__:hello。hello 携带发送者身份、纪元和 Master 配置,收到陌生 runid 后,Sentinel 才把对方加入这个复制组的 peer 视图。
Pub/Sub 用于发现 peer 和传播 Master 配置。peer 已知后,周期 PING、SENTINEL is-master-down-by-addr、下线报告和选举投票都走 Sentinel 间直连。每个实例独立保存当前纪元、Master 配置纪元和本轮投票;多数派授权的是一次 failover 的执行者,不是长期存在的 Sentinel Master。
自动发现也有边界。hello 通告的地址必须能被其他 Sentinels 访问,NAT 或端口映射环境要设置 sentinel announce-ip 和 sentinel announce-port;启用 ACL 后,peer 直连使用 sentinel sentinel-user 和 sentinel sentinel-pass。Sentinel 不会因为 peer 长期失联就自动忘记它,永久移除实例时要先停止目标进程,再在其余 Sentinels 上间隔至少 30 秒执行 SENTINEL RESET <master-name>,最后检查成员数与 CKQUORUM。
4. 实验:观察 Sentinel 的行为#
本章命令中的
pc是下面这个 Podman Compose 包装函数:pc() { PODMAN_COMPOSE_WARNING_LOGS=false podman compose \ -p redis-sentinel-lab -f compose.yaml "$@" }
以下结果实测于 2026-08-04,环境为 Podman 5.5.2、podman-compose 1.5.0 和 Redis 8.10.0。每组实验都先执行 ./run.sh start,删除上一组实验的容器和数据卷,恢复为 redis-1 主、redis-2 和 redis-3 从的固定拓扑。
4.1 观察节点发现与周期监控#
本实验要验证两件事:三个 Sentinel 是否发现了同一个 Master、两个 Replicas 和彼此两个 peers,并且 quorum 与故障转移授权条件均可达;Sentinel 是否会通过周期性的 PING、INFO 和 __sentinel__:hello 发布持续维护这份拓扑视图。
执行命令:
for sentinel in sentinel-1 sentinel-2 sentinel-3; do
printf '\n# %s\n' "$sentinel"
printf 'myid='
pc exec -T "$sentinel" redis-cli --raw -p 26379 \
SENTINEL myid
printf 'master='
pc exec -T "$sentinel" redis-cli --raw -p 26379 \
SENTINEL get-master-addr-by-name mymaster | paste -sd ':' -
printf 'ckquorum='
pc exec -T "$sentinel" redis-cli --raw -p 26379 \
SENTINEL ckquorum mymaster
pc exec -T "$sentinel" redis-cli --raw -p 26379 \
SENTINEL replicas mymaster | tr -d '\r' | \
awk '$0 == "name" { replicas++ } END { print "replicas=" replicas + 0 }'
pc exec -T "$sentinel" redis-cli --raw -p 26379 \
SENTINEL sentinels mymaster | tr -d '\r' | \
awk '$0 == "name" { peers++ } END { print "peer-sentinels=" peers + 0 }'
done节点发现结果
# sentinel-1
myid=8fdda9f1f59b9679c4bc23846628f8d1ae384df8
master=redis-1:6379
ckquorum=OK 3 usable Sentinels. Quorum and failover authorization can be reached
replicas=2
peer-sentinels=2
# sentinel-2
myid=cab4401ab1d00db3db2ccd0adcfcd5eecaadad0c
master=redis-1:6379
ckquorum=OK 3 usable Sentinels. Quorum and failover authorization can be reached
replicas=2
peer-sentinels=2
# sentinel-3
myid=22215985d8e613ab86d7ebee587babee40d0f171
master=redis-1:6379
ckquorum=OK 3 usable Sentinels. Quorum and failover authorization can be reached
replicas=2
peer-sentinels=2执行命令:
pc exec -T sentinel-1 redis-cli --raw -p 26379 \
SENTINEL sentinels mymaster | tr -d '\r' | \
awk '
$0 == "name" {
peer++
if (peer > 1) print ""
printf "# peer %d\n", peer
}
{ print }
'Sentinel peer 详情
# peer 1
name
cab4401ab1d00db3db2ccd0adcfcd5eecaadad0c
ip
10.89.1.6
port
26379
runid
cab4401ab1d00db3db2ccd0adcfcd5eecaadad0c
flags
sentinel
link-pending-commands
0
link-refcount
1
last-ping-sent
0
last-ok-ping-reply
82
last-ping-reply
82
down-after-milliseconds
5000
last-hello-message
218
voted-leader
?
voted-leader-epoch
0
# peer 2
name
22215985d8e613ab86d7ebee587babee40d0f171
ip
10.89.1.7
port
26379
runid
22215985d8e613ab86d7ebee587babee40d0f171
flags
sentinel
link-pending-commands
0
link-refcount
1
last-ping-sent
0
last-ok-ping-reply
82
last-ping-reply
82
down-after-milliseconds
5000
last-hello-message
1777
voted-leader
?
voted-leader-epoch
0执行命令:
printf '# redis-1 MONITOR\n'
pc exec redis-1 redis-cli MONITORMONITOR 输出
# redis-1 MONITOR
OK
1785822963.456740 [0 10.89.1.7:42252] "PING"
1785822963.514380 [0 10.89.1.5:41580] "PING"
1785822963.987368 [0 10.89.1.6:47060] "PING"
1785822964.286629 [0 10.89.1.5:41580] "PUBLISH" "__sentinel__:hello" "10.89.1.5,26379,8fdda9f1f59b9679c4bc23846628f8d1ae384df8,0,mymaster,redis-1,6379,0"
1785822964.519722 [0 10.89.1.5:41580] "PING"
1785822964.528096 [0 10.89.1.7:42252] "PING"
1785822964.626970 [0 10.89.1.6:47060] "PUBLISH" "__sentinel__:hello" "10.89.1.6,26379,cab4401ab1d00db3db2ccd0adcfcd5eecaadad0c,0,mymaster,redis-1,6379,0"
1785822964.651873 [0 10.89.1.7:42252] "PUBLISH" "__sentinel__:hello" "10.89.1.7,26379,22215985d8e613ab86d7ebee587babee40d0f171,0,mymaster,redis-1,6379,0"
1785822965.018314 [0 127.0.0.1:39396] "ping"
1785822965.024221 [0 10.89.1.6:47060] "PING"
1785822965.539056 [0 10.89.1.7:42252] "PING"
1785822965.581917 [0 10.89.1.5:41580] "PING"
1785822966.074615 [0 10.89.1.6:47060] "PING"
1785822966.369419 [0 10.89.1.5:41580] "PUBLISH" "__sentinel__:hello" "10.89.1.5,26379,8fdda9f1f59b9679c4bc23846628f8d1ae384df8,0,mymaster,redis-1,6379,0"
1785822966.580222 [0 10.89.1.7:42252] "PING"
1785822966.606710 [0 10.89.1.5:41580] "PING"
1785822966.650339 [0 10.89.1.6:47060] "PUBLISH" "__sentinel__:hello" "10.89.1.6,26379,cab4401ab1d00db3db2ccd0adcfcd5eecaadad0c,0,mymaster,redis-1,6379,0"
1785822966.682308 [0 10.89.1.7:42252] "PUBLISH" "__sentinel__:hello" "10.89.1.7,26379,22215985d8e613ab86d7ebee587babee40d0f171,0,mymaster,redis-1,6379,0"
1785822967.078837 [0 10.89.1.6:47060] "PING"
1785822967.610383 [0 10.89.1.7:42252] "PING"
1785822967.639515 [0 10.89.1.5:41580] "PING"
1785822968.099179 [0 10.89.1.6:47060] "PING"
1785822968.120448 [0 127.0.0.1:59880] "ping"
1785822968.418590 [0 10.89.1.5:41580] "PUBLISH" "__sentinel__:hello" "10.89.1.5,26379,8fdda9f1f59b9679c4bc23846628f8d1ae384df8,0,mymaster,redis-1,6379,0"
1785822968.659030 [0 10.89.1.6:47060] "PUBLISH" "__sentinel__:hello" "10.89.1.6,26379,cab4401ab1d00db3db2ccd0adcfcd5eecaadad0c,0,mymaster,redis-1,6379,0"
1785822968.659443 [0 10.89.1.7:42252] "PING"
1785822968.718104 [0 10.89.1.5:41580] "PING"
1785822968.736650 [0 10.89.1.7:42252] "PUBLISH" "__sentinel__:hello" "10.89.1.7,26379,22215985d8e613ab86d7ebee587babee40d0f171,0,mymaster,redis-1,6379,0"
1785822969.109587 [0 10.89.1.6:47060] "PING"
1785822969.671602 [0 10.89.1.7:42252] "PING"
1785822969.752220 [0 10.89.1.5:41580] "INFO"
1785822969.752246 [0 10.89.1.5:41580] "PING"
1785822969.955942 [0 10.89.1.6:47060] "INFO"
1785822970.018776 [0 10.89.1.7:42252] "INFO"
1785822970.178119 [0 10.89.1.6:47060] "PING"
1785822970.506924 [0 10.89.1.5:41580] "PUBLISH" "__sentinel__:hello" "10.89.1.5,26379,8fdda9f1f59b9679c4bc23846628f8d1ae384df8,0,mymaster,redis-1,6379,0"
1785822970.692314 [0 10.89.1.7:42252] "PING"
1785822970.743760 [0 10.89.1.6:47060] "PUBLISH" "__sentinel__:hello" "10.89.1.6,26379,cab4401ab1d00db3db2ccd0adcfcd5eecaadad0c,0,mymaster,redis-1,6379,0"
1785822970.761169 [0 10.89.1.7:42252] "PUBLISH" "__sentinel__:hello" "10.89.1.7,26379,22215985d8e613ab86d7ebee587babee40d0f171,0,mymaster,redis-1,6379,0"
1785822970.851777 [0 10.89.1.5:41580] "PING"
1785822971.002536 [0 127.0.0.1:59902] "ping"
1785822971.236768 [0 10.89.1.6:47060] "PING"
1785822971.742526 [0 10.89.1.7:42252] "PING"
1785822971.873466 [0 10.89.1.5:41580] "PING"
1785822972.282215 [0 10.89.1.6:47060] "PING"
1785822972.560418 [0 10.89.1.5:41580] "PUBLISH" "__sentinel__:hello" "10.89.1.5,26379,8fdda9f1f59b9679c4bc23846628f8d1ae384df8,0,mymaster,redis-1,6379,0"
1785822972.752865 [0 10.89.1.7:42252] "PING"
1785822972.790844 [0 10.89.1.6:47060] "PUBLISH" "__sentinel__:hello" "10.89.1.6,26379,cab4401ab1d00db3db2ccd0adcfcd5eecaadad0c,0,mymaster,redis-1,6379,0"
1785822972.837558 [0 10.89.1.7:42252] "PUBLISH" "__sentinel__:hello" "10.89.1.7,26379,22215985d8e613ab86d7ebee587babee40d0f171,0,mymaster,redis-1,6379,0"
1785822972.930431 [0 10.89.1.5:41580] "PING"实验结论:
- 三个 Sentinel 对 Master、两个 Replicas 和两个 peers 的视图一致。
CKQUORUM确认 quorum 与故障转移授权均可达。- peer 记录中的
name与runid和各实例的myid一致,且没有下线或断连标记。 - 三个 Sentinel 会持续向 Redis 发送
PING、周期执行INFO,并通过__sentinel__:hello发布自身和 Master 配置。
MONITOR 只能观察 Sentinel 与 Redis 之间的命令,不包含 Sentinel 之间的直连协调流量。
4.2 记录一次自动故障转移#
本实验停止当前 Master,持续查询三个 Sentinel,直到它们返回同一个新地址。先动态获取 Master,避免把初始节点写死到停止命令中。
执行命令:
FAILED_MASTER=$(pc exec -T sentinel-1 redis-cli --raw -p 26379 \
SENTINEL get-master-addr-by-name mymaster | sed -n '1p')
printf '# 停止当前 Master\n'
printf 'old master: %s\n' "$FAILED_MASTER"
pc stop "$FAILED_MASTER"
printf '\n# 轮询三个 Sentinel 的 Master 视图\n'
NEW_MASTER=''
for round in $(seq 0 45); do
VIEW_1=$(pc exec -T sentinel-1 redis-cli --raw -p 26379 \
SENTINEL get-master-addr-by-name mymaster 2>/dev/null | sed -n '1p')
VIEW_2=$(pc exec -T sentinel-2 redis-cli --raw -p 26379 \
SENTINEL get-master-addr-by-name mymaster 2>/dev/null | sed -n '1p')
VIEW_3=$(pc exec -T sentinel-3 redis-cli --raw -p 26379 \
SENTINEL get-master-addr-by-name mymaster 2>/dev/null | sed -n '1p')
printf '[round %02d] sentinel-1=%s sentinel-2=%s sentinel-3=%s\n' \
"$round" "$VIEW_1" "$VIEW_2" "$VIEW_3"
if [ -n "$VIEW_1" ] && \
[ "$VIEW_1" = "$VIEW_2" ] && \
[ "$VIEW_2" = "$VIEW_3" ] && \
[ "$VIEW_1" != "$FAILED_MASTER" ]; then
NEW_MASTER=$VIEW_1
break
fi
sleep 1
done
test -n "$NEW_MASTER"
printf 'new master: %s\n' "$NEW_MASTER"输出:
# 停止当前 Master
old master: redis-1
redis-sentinel-lab_redis-1_1
# 轮询三个 Sentinel 的 Master 视图
[round 00] sentinel-1=redis-1 sentinel-2=redis-1 sentinel-3=redis-1
[round 01] sentinel-1=redis-1 sentinel-2=redis-1 sentinel-3=redis-1
[round 02] sentinel-1=redis-1 sentinel-2=redis-1 sentinel-3=redis-1
[round 03] sentinel-1=redis-1 sentinel-2=redis-1 sentinel-3=redis-1
[round 04] sentinel-1=redis-2 sentinel-2=redis-2 sentinel-3=redis-2
new master: redis-2可以看到在 round4(~5s)的时候就完成了新的选主,这个时间也符合实验环境 down-after-milliseconds mymaster 5000 的设置,这时候检查全部 Sentinel 中的相关日志:
执行命令:
for sentinel in sentinel-1 sentinel-2 sentinel-3; do
printf '\n# %s\n' "$sentinel"
pc logs "$sentinel" 2>&1 | \
grep -E '\+(sdown|odown|new-epoch|try-failover|vote-for-leader|elected-leader|selected-slave|promoted-slave|switch-master)'
done故障转移事件日志
# sentinel-1
1:X 04 Aug 2026 08:28:44.398 # +sdown master mymaster redis-1 6379
1:X 04 Aug 2026 08:28:44.506 # +new-epoch 1
1:X 04 Aug 2026 08:28:44.506 # +vote-for-leader cab4401ab1d00db3db2ccd0adcfcd5eecaadad0c 1
1:X 04 Aug 2026 08:28:45.477 # +odown master mymaster redis-1 6379 #quorum 3/2
1:X 04 Aug 2026 08:28:46.711 # +switch-master mymaster redis-1 6379 redis-2 6379
# sentinel-2
1:X 04 Aug 2026 08:28:44.441 # +sdown master mymaster redis-1 6379
1:X 04 Aug 2026 08:28:44.499 # +odown master mymaster redis-1 6379 #quorum 2/2
1:X 04 Aug 2026 08:28:44.499 # +new-epoch 1
1:X 04 Aug 2026 08:28:44.499 # +try-failover master mymaster redis-1 6379
1:X 04 Aug 2026 08:28:44.502 # +vote-for-leader cab4401ab1d00db3db2ccd0adcfcd5eecaadad0c 1
1:X 04 Aug 2026 08:28:44.566 # +elected-leader master mymaster redis-1 6379
1:X 04 Aug 2026 08:28:44.645 # +selected-slave slave redis-2:6379 redis-2 6379 @ mymaster redis-1 6379
1:X 04 Aug 2026 08:28:45.560 # +promoted-slave slave redis-2:6379 redis-2 6379 @ mymaster redis-1 6379
1:X 04 Aug 2026 08:28:46.648 # +switch-master mymaster redis-1 6379 redis-2 6379
# sentinel-3
1:X 04 Aug 2026 08:28:44.478 # +sdown master mymaster redis-1 6379
1:X 04 Aug 2026 08:28:44.506 # +new-epoch 1
1:X 04 Aug 2026 08:28:44.506 # +vote-for-leader cab4401ab1d00db3db2ccd0adcfcd5eecaadad0c 1
1:X 04 Aug 2026 08:28:44.534 # +odown master mymaster redis-1 6379 #quorum 3/2
1:X 04 Aug 2026 08:28:46.711 # +switch-master mymaster redis-1 6379 redis-2 6379可以看到,sentinel-2 是本轮执行者,它选中并晋升了 redis-2。
最后启动旧 Master,等待 Sentinel 把它重配为新 Master 的 Replica。
执行命令:
printf '# 启动旧 Master\n'
pc start "$FAILED_MASTER"
printf '\n# 等待旧 Master 恢复为 Replica\n'
for attempt in $(seq 1 60); do
if pc exec -T "$FAILED_MASTER" redis-cli --raw INFO replication \
2>/dev/null | tr -d '\r' | \
awk -F: '
$1 == "role" { role=$2 }
$1 == "master_link_status" { link=$2 }
END { exit !(role == "slave" && link == "up") }
'
then
printf 'recovered after %ss\n' "$attempt"
break
fi
sleep 1
done
printf '\n# 旧 Master 的复制状态\n'
pc exec -T "$FAILED_MASTER" redis-cli --raw INFO replication | \
sed -n '/^role:/p;/^master_host:/p;/^master_link_status:/p'
printf '\n# 三个 Sentinel 的 Master 视图\n'
for sentinel in sentinel-1 sentinel-2 sentinel-3; do
printf '%s -> ' "$sentinel"
pc exec -T "$sentinel" redis-cli --raw -p 26379 \
SENTINEL get-master-addr-by-name mymaster | paste -sd ':' -
done输出:
# 启动旧 Master
redis-sentinel-lab_redis-1_1
# 等待旧 Master 恢复为 Replica
recovered after 33s
# 旧 Master 的复制状态
role:slave
master_host:redis-2
master_link_status:up
# 三个 Sentinel 的 Master 视图
sentinel-1 -> redis-2:6379
sentinel-2 -> redis-2:6379
sentinel-3 -> redis-2:6379实验结论:
redis-1停止后,三个 Sentinel 先后形成 SDOWN 和 ODOWN。sentinel-2获得选票,选中redis-2并完成晋升。- 第 5 轮查询时,三个 Sentinel 的地址视图已经收敛到
redis-2,新 Master 读写正常。 - 旧 Master 启动后约 33 秒以 Replica 身份恢复。
4.3 观察恢复中 Replica 的 Sentinel 状态#
Sentinel 能发现一个 Replica,不代表这个节点已经完成同步。本实验利用 64 KiB backlog 和 30 秒 diskless sync delay 拉长恢复窗口。
先停止当前 Master,等待三个 Sentinels 对新 Master 达成一致:
执行命令:
RECOVERING_NODE=$(pc exec -T sentinel-1 redis-cli --raw -p 26379 \
SENTINEL get-master-addr-by-name mymaster | sed -n '1p')
printf '# 停止当前 Master: %s\n' "$RECOVERING_NODE"
pc stop "$RECOVERING_NODE"
printf '\n# 等待三个 Sentinel 的 Master 视图收敛\n'
for attempt in $(seq 0 45); do
MASTER_1=$(pc exec -T sentinel-1 redis-cli --raw -p 26379 \
SENTINEL get-master-addr-by-name mymaster | paste -sd ':' -)
MASTER_2=$(pc exec -T sentinel-2 redis-cli --raw -p 26379 \
SENTINEL get-master-addr-by-name mymaster | paste -sd ':' -)
MASTER_3=$(pc exec -T sentinel-3 redis-cli --raw -p 26379 \
SENTINEL get-master-addr-by-name mymaster | paste -sd ':' -)
if [ "$MASTER_1" = "$MASTER_2" ] && \
[ "$MASTER_2" = "$MASTER_3" ] && \
[ "$MASTER_1" != "$RECOVERING_NODE:6379" ]; then
printf 'consensus reached at round %s\n' "$attempt"
break
fi
sleep 1
done
NEW_MASTER=${MASTER_1%:6379}
printf 'new master: %s\n' "$NEW_MASTER"输出:
# 停止当前 Master: redis-1
redis-sentinel-lab_redis-1_1
# 等待三个 Sentinel 的 Master 视图收敛
consensus reached at round 4
new master: redis-3旧节点仍停止时,向新 Master 写入约 10 MB 数据,使偏移差超过实验 backlog:
执行命令:
printf '# 写入实验键\n'
pc exec -T sentinel-1 redis-cli -h "$NEW_MASTER" \
SET lab:recovery-window ready
printf '\n# 写入约 10 MB 数据\n'
pc exec -T sentinel-1 redis-benchmark -h "$NEW_MASTER" \
-t set -n 10000 -d 1024 -r 1000000 -q输出:
# 写入实验键
OK
# 写入约 10 MB 数据
SET: 175438.59 requests per second, p50=0.207 msec在终端 A 运行观察循环。它只抽取恢复节点在 Sentinel 中的 flags 和复制链路,同时查询该 Redis 进程自己的复制状态:
执行命令:
RECOVERING_NODE=redis-1
NEW_MASTER=$(pc exec -T sentinel-1 redis-cli --raw -p 26379 \
SENTINEL get-master-addr-by-name mymaster | sed -n '1p')
while :; do
date '+%H:%M:%S'
pc exec -T sentinel-1 redis-cli --raw -p 26379 \
SENTINEL replicas mymaster 2>/dev/null | tr -d '\r' | \
awk -v target="$RECOVERING_NODE" '
function emit() {
if (name ~ ("^" target ":") || ip == target) {
printf "name=%s ip=%s flags=%s link=%s link-down-ms=%s\n", \
name, ip, flags, link, down
}
}
$0 == "name" {
emit()
name=""; ip=""; flags=""; link=""; down=""
getline; name=$0; next
}
$0 == "ip" { getline; ip=$0; next }
$0 == "flags" { getline; flags=$0; next }
$0 == "master-link-status" { getline; link=$0; next }
$0 == "master-link-down-time" { getline; down=$0; next }
END { emit() }
'
pc exec -T "$RECOVERING_NODE" redis-cli --raw INFO replication \
2>/dev/null | \
sed -n '/^role:/p;/^master_host:/p;/^master_link_status:/p;/^master_sync_in_progress:/p'
sleep 1
done在终端 B 启动旧节点:
执行命令:
pc start redis-1输出中每秒变化的 link-down 时长和重复状态已经删去,只保留状态转换:
输出:
# 恢复节点状态转换
[00s] sentinel: flags=slave,disconnected link=err link-down-ms=0 | redis: role=master
[01s] sentinel: flags=slave link=err link-down-ms=0 | redis: role=master
[11s] sentinel: flags=slave link=err link-down-ms=-1000 | redis: role=slave master=redis-3 link=down sync=0
[42s] sentinel: flags=slave link=ok link-down-ms=0 | redis: role=slave master=redis-3 link=up sync=0等恢复节点显示 master_link_status:up 后,从恢复节点读取实验键,再从新 Master 的日志确认实际同步路径。
执行命令:
printf '# 从恢复后的 Replica 读取实验键\n'
pc exec -T "$RECOVERING_NODE" redis-cli GET lab:recovery-window
printf '\n# 新 Master 的同步日志\n'
pc logs "$NEW_MASTER" 2>&1 | \
grep -E 'Full resync|BGSAVE|diskless|Synchronization with replica'输出:
# 从恢复后的 Replica 读取实验键
ready
# 新 Master 的同步日志
1:M 04 Aug 2026 08:36:01.434 * Full resync requested by replica redis-1:6379 (rdb-channel)
1:M 04 Aug 2026 08:36:01.434 * Delay next BGSAVE for diskless SYNC
1:M 04 Aug 2026 08:36:31.791 * Starting BGSAVE for SYNC with target: replicas sockets (rdb-channel)
54:C 04 Aug 2026 08:36:31.835 * BGSAVE done, 9962 keys saved, 0 keys skipped, 10399539 bytes written.
1:M 04 Aug 2026 08:36:31.859 * Synchronization with replica redis-1:6379 succeeded实验结论:
- 恢复节点尚未启动时,Sentinel 已经在 Replicas 列表中保留了它,状态是
slave,disconnected、链路是err。 - 节点启动后先以
role=master运行;约 11 秒后被重配为slave,但直连复制链路仍为down。这时master_sync_in_progress=0,同样不能说明节点可读。 - Master 日志记录了 full resync,其中约 30 秒花在 diskless sync 等待。约 42 秒时,Redis 报告
master_link_status=up,Sentinel 才报告link=ok,实验键也能从该 Replica 读出。 - 节点出现在
SENTINEL REPLICAS中,不代表它已经完成数据恢复并可对外提供服务。
本次实验中,本地 10 MB 数据的实际传输在一次轮询间隔内完成,因此没有采样到 master_sync_in_progress=1。
4.4 执行一次计划内故障转移#
计划切换不需要先制造 ODOWN。Redis 8.10 的 SENTINEL FAILOVER 会进入强制故障转移路径,跳过 ODOWN 判断和正常领导者选举授权。操作前执行 CKQUORUM 是运维安全门,用来暴露 Sentinel 数量或通信异常,不是该命令执行的前置条件。还要记录候选 Replicas 的复制状态。实验环境没有持续写入者,下面的检查不能替代生产中的写入排空:
执行命令:
printf '# 检查 Sentinel 可用性\n'
pc exec -T sentinel-1 redis-cli -p 26379 SENTINEL CKQUORUM mymaster
printf '\n# 查询当前 Master\n'
OLD_MASTER=$(pc exec -T sentinel-1 redis-cli --raw -p 26379 \
SENTINEL get-master-addr-by-name mymaster | sed -n '1p')
printf 'old master: %s\n' "$OLD_MASTER"
for node in redis-1 redis-2 redis-3; do
printf '\n# %s: INFO replication\n' "$node"
pc exec -T "$node" redis-cli --raw INFO replication | \
sed -n '/^role:/p;/^master_host:/p;/^master_link_status:/p;/^master_repl_offset:/p;/^slave_repl_offset:/p'
done切换前复制状态
# 检查 Sentinel 可用性
OK 3 usable Sentinels. Quorum and failover authorization can be reached
# 查询当前 Master
old master: redis-1
# redis-1: INFO replication
role:master
master_repl_offset:9062
# redis-2: INFO replication
role:slave
master_host:redis-1
master_link_status:up
slave_repl_offset:9062
master_repl_offset:9062
# redis-3: INFO replication
role:slave
master_host:redis-1
master_link_status:up
slave_repl_offset:9062
master_repl_offset:9062通过任一 Sentinel 发起受控切换,再等待三个地址视图一致:
执行命令:
printf '# 发起计划切换\n'
pc exec -T sentinel-1 redis-cli -p 26379 SENTINEL FAILOVER mymaster
printf '\n# 等待三个 Sentinel 的 Master 视图收敛\n'
for round in $(seq 0 45); do
MASTER_1=$(pc exec -T sentinel-1 redis-cli --raw -p 26379 \
SENTINEL get-master-addr-by-name mymaster | sed -n '1p')
MASTER_2=$(pc exec -T sentinel-2 redis-cli --raw -p 26379 \
SENTINEL get-master-addr-by-name mymaster | sed -n '1p')
MASTER_3=$(pc exec -T sentinel-3 redis-cli --raw -p 26379 \
SENTINEL get-master-addr-by-name mymaster | sed -n '1p')
printf '[round %02d] sentinel-1=%s sentinel-2=%s sentinel-3=%s\n' \
"$round" "$MASTER_1" "$MASTER_2" "$MASTER_3"
if [ "$MASTER_1" = "$MASTER_2" ] && \
[ "$MASTER_2" = "$MASTER_3" ] && \
[ "$MASTER_1" != "$OLD_MASTER" ]; then
NEW_MASTER=$MASTER_1
break
fi
sleep 1
done
printf 'master moved: %s -> %s\n' "$OLD_MASTER" "$NEW_MASTER"
printf '\n# 验证新 Master 读写\n'
pc exec -T sentinel-1 redis-cli -h "$NEW_MASTER" \
SET lab:planned-failover ok
pc exec -T sentinel-1 redis-cli -h "$NEW_MASTER" \
GET lab:planned-failover输出:
# 发起计划切换
OK
# 等待三个 Sentinel 的 Master 视图收敛
[round 00] sentinel-1=redis-1 sentinel-2=redis-1 sentinel-3=redis-1
[round 01] sentinel-1=redis-3 sentinel-2=redis-3 sentinel-3=redis-3
master moved: redis-1 -> redis-3
# 验证新 Master 读写
OK
ok旧 Master 没有停止,但 Sentinel 仍会把它重配为 Replica。等待复制链路恢复并提取事件:
执行命令:
printf '# 等待旧 Master 恢复为 Replica\n'
for attempt in $(seq 1 60); do
if pc exec -T "$OLD_MASTER" redis-cli --raw INFO replication \
2>/dev/null | tr -d '\r' | \
awk -F: '
$1 == "role" { role=$2 }
$1 == "master_link_status" { link=$2 }
END { exit !(role == "slave" && link == "up") }
'
then
printf 'old master recovered after %ss\n' "$attempt"
break
fi
sleep 1
done
printf '\n# 旧 Master 的复制状态\n'
pc exec -T "$OLD_MASTER" redis-cli --raw INFO replication | \
sed -n '/^role:/p;/^master_host:/p;/^master_link_status:/p'
printf '\n# 三个 Redis 的最终角色\n'
for node in redis-1 redis-2 redis-3; do
printf '%s: ' "$node"
pc exec -T "$node" redis-cli --raw ROLE | sed -n '1p'
done
printf '\n# Sentinel 故障转移事件\n'
for sentinel in sentinel-1 sentinel-2 sentinel-3; do
printf '\n# %s\n' "$sentinel"
pc logs "$sentinel" 2>&1 | \
grep -E '\+(sdown|odown|new-epoch|try-failover|elected-leader|selected-slave|promoted-slave|switch-master)'
done计划切换事件日志
# 等待旧 Master 恢复为 Replica
old master recovered after 8s
# 旧 Master 的复制状态
role:slave
master_host:redis-3
master_link_status:up
# 三个 Redis 的最终角色
redis-1: slave
redis-2: slave
redis-3: master
# Sentinel 故障转移事件
# sentinel-1
1:X 04 Aug 2026 08:37:57.589 # +new-epoch 1
1:X 04 Aug 2026 08:37:57.589 # +try-failover master mymaster redis-1 6379
1:X 04 Aug 2026 08:37:57.620 # +elected-leader master mymaster redis-1 6379
1:X 04 Aug 2026 08:37:57.722 # +selected-slave slave redis-3:6379 redis-3 6379 @ mymaster redis-1 6379
1:X 04 Aug 2026 08:37:58.662 # +promoted-slave slave redis-3:6379 redis-3 6379 @ mymaster redis-1 6379
1:X 04 Aug 2026 08:38:10.099 # +switch-master mymaster redis-1 6379 redis-3 6379
# sentinel-2
1:X 04 Aug 2026 08:37:57.919 # +new-epoch 1
1:X 04 Aug 2026 08:37:58.723 # +switch-master mymaster redis-1 6379 redis-3 6379
# sentinel-3
1:X 04 Aug 2026 08:37:57.919 # +new-epoch 1
1:X 04 Aug 2026 08:37:58.723 # +switch-master mymaster redis-1 6379 redis-3 6379实验结论:
- 切换前三个 Redis 的复制偏移量都是 9062,两个 Replica 链路也是
up。 SENTINEL FAILOVER直接返回OK,日志中没有 SDOWN 或 ODOWN;sentinel-1选中并晋升了redis-3。- 下一轮查询时,三个 Sentinel 都已经返回
redis-3。 - 旧 Master 始终没有停机,约 8 秒后以 Replica 身份恢复复制。
日志仍出现历史命名的 +elected-leader,但它不能证明这次强制切换走了自动路径的多数派授权。各 Sentinel 的 +switch-master 时间也不完全一致,不能把某一个进程的日志时间直接当成客户端地址切换时间。生产操作仍需要排空写入、确认候选进度,并单独验证客户端连接池的刷新行为。
5. 影响 Sentinel 行为的参数#
完整配置请先看 Redis 官方的 Redis 8.10.0
sentinel.conf;参数细节和版本变化以 Redis Sentinel 官方文档 为准。
本节只保留会直接改变故障判定、故障转移或新 Master 选择结果的参数。认证、目录、脚本、DNS、NAT 和 TLS 等部署配置不在这里展开。表中的重要程度是本文针对生产环境的判断,不是 Redis 官方评级;本节没有“低”级参数。
| 参数 | 重要程度 | Redis 8.10.0 默认值 / 推荐起点 | 作用 | 生产注意事项 |
|---|---|---|---|---|
sentinel monitor <name> <host> <port> <quorum> |
高 | 无默认;三个 Sentinel 推荐 quorum 2 |
创建监控组,并规定形成 ODOWN 所需的同意数 | quorum 先控制 ODOWN;如果高于多数派,也会抬高执行者的授权门槛 |
sentinel down-after-milliseconds <name> <ms> |
高 | 默认 30000 ms;稳定同地域网络可从 10000 ms 起测 |
节点持续没有可接受响应达到该时长后进入 SDOWN | 过小容易把网络抖动、宿主机暂停或 Redis 主线程阻塞当成故障,过大则延迟切换 |
sentinel master-reboot-down-after-period <name> <ms> |
中 | 默认及推荐起点 0 |
Master 重启后,Sentinel 接受 -LOADING 响应的最长时间 |
0 表示持续接受 -LOADING,不会因此发起切换;设置非零值前要先测出正常加载耗时 |
sentinel failover-timeout <name> <ms> |
高 | 默认及推荐起点 180000 ms |
控制故障转移重试、晋升超时判断和 Replica 重配期限 | 它不是一次切换的简单总时限;直接调小可能让仍在推进的故障转移重复进入新轮次 |
sentinel parallel-syncs <name> <count> |
中 | 默认及推荐起点 1 |
新 Master 产生后,同时重配的 Replicas 数量 | 调高可以加快拓扑恢复,但会让更多只读节点同时不可用,并集中消耗新 Master 的资源 |
replica-priority |
高 | 默认 100;按候选能力设置正整数 |
Sentinel 对候选 Replicas 排序时,数值较小者优先 | 该参数写在 redis.conf;0 表示永不晋升,也会减少可用候选 |
quorum 先用于把 Master 从 SDOWN 推进到 ODOWN。自动故障转移选举执行者时,授权门槛是 max(floor(N / 2) + 1, quorum),其中 N 是当前 Master 视图中已知的 Sentinel 总数,包含自身。因此,把三个 Sentinel 的 quorum 从 2 改成 1,仍然至少需要 2 票。手动执行 SENTINEL FAILOVER 不受这个授权条件限制。
down-after-milliseconds 应覆盖正常运行时的尾部延迟、短时丢包和最长宿主机暂停。第四节为了缩短实验时间使用了 5 秒,生产环境不要直接照抄。
failover-timeout 在状态机的多个阶段复用。没有实际事件日志证明重试间隔过长时,保留默认值比按目标 RTO 直接压缩更稳妥。
parallel-syncs 取决于切换期间要保留多少只读容量,以及一次全量同步对新 Master 的影响。有两个 Replicas 且业务依赖副本读取时,建议先保留 1。
Sentinel 会先排除不健康的 Replicas,再按 priority、复制偏移量和 run ID 排序。priority 排在复制进度之前:如果所有节点都具备同等接管能力,保持相同值即可;只有明确希望某个节点优先或禁止晋升时才区分配置。
对于一个 Master、两个 Replicas、三个 Sentinel 的同地域部署,可以先从下面这组配置开始演练:
sentinel monitor mymaster 10.0.1.10 6379 2
sentinel down-after-milliseconds mymaster 10000
sentinel master-reboot-down-after-period mymaster 0
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000可能参与晋升的 Redis 节点先保持默认优先级:
replica-priority 100这组值只是演练起点。上线前至少验证一次 Master 故障:记录 SDOWN、ODOWN、选举、晋升和 Replica 重配的耗时,确认被选中的 Replica 符合预期,再根据观测结果调整参数。
6. 总结#
Sentinel 是 Redis 主从复制之外的分布式控制面。 每个 Sentinel 独立监控拓扑,先记录 SDOWN,再通过 quorum 形成 ODOWN。进入自动故障转移后,Sentinels 按纪元授权一个执行者,由它选择并晋升 Replica、重配其余节点,客户端最后重新发现 Master。
Redis 将故障判断分散给多个 Sentinel,只把一次切换交给一个执行者。 单个进程无法可靠区分节点宕机和网络分区,因此 ODOWN 需要多个观察者;自动故障转移还要取得多数派授权,同一纪元只由一个 Sentinel 协调。数据复制继续留在 Redis 主从链路,Sentinel 不进入数据路径。
Sentinel 的优点是能以较低成本为现有主从复制补上自动故障转移。 它无需代理数据流,可以按健康状态、优先级和复制进度选择候选,并让旧 Master 作为 Replica 回归。多个 Sentinel 跨故障域部署后,也避免了单个监控进程成为控制面单点。
Sentinel 优先恢复可用性,因此保留了异步复制的一致性风险。 未同步写入仍可能丢失,分区中的旧 Master 也可能继续接收写入。实际恢复依赖 Sentinel 部署、地址通告、配置持久化和客户端重连;它不负责读副本准入,也不解决分片或跨地域强一致。
参考资料#
- Redis Sentinel 官方文档
- High availability with Sentinel client specification
SENTINEL FAILOVERSENTINEL CKQUORUMSENTINEL SET- Redis 8.10.0
redis.conf - Redis 8.10.0
sentinel.conf - Redis 8.10.0 Sentinel 实例发现与周期通信
- Redis 8.10.0 Sentinel 下线询问与领导者投票
- Redis 8.10.0 Sentinel 晋升确认与配置纪元更新
- Redis 8.10.0 Sentinel failover 状态机