Redis 从入门到精通(五):哨兵模式(Sentinel)—— 自动故障转移的完整原理与实战


一、Sentinel 是什么?为什么需要它?

1.1 从主从复制到高可用的最后一公里

主从架构:

Master(主库) ← 写请求
    ↓
Slave-1, Slave-2, Slave-3(从库) ← 读请求

如果主库宕机了:

  • 写请求全部失败
  • 需要手动选一个从库升级为主库
  • 需要通知所有客户端新的主库地址
  • 需要把其他从库指向新主库

这一套操作如果靠人工,少则几分钟,多则半小时。Sentinel 把这个过程自动化到了秒级

1.2 Sentinel 的四大核心能力

4. 配置提供者(Configuration Provider)

客户端通过 Sentinel
发现当前主库地址

3. 自动故障转移(Failover)

主库下线 → 选举新主库
→ 切换从库指向
→ 通知客户端

2. 通知(Notification)

通过 Pub/Sub 或 API
通知管理员/客户端
节点状态变化

1. 监控(Monitoring)

定期 PING 主库和从库
检查存活状态

Sentinel 集群

Sentinel-1

Sentinel-2

Sentinel-3

1.3 Sentinel 本身的特点

特性 说明
独立进程 Sentinel 是独立于 Redis 的进程(redis-sentinelredis-server --sentinel
不存业务数据 Sentinel 只做监控和协调,不存储业务数据
需要集群部署 至少 3 个 Sentinel 实例(避免单点故障和脑裂)
多数派决策 故障判断和选举需要过半数 Sentinel 同意
基于 Pub/Sub Sentinel 之间通过 Redis 的 Pub/Sub 发现彼此

二、Sentinel 的监控机制:如何发现节点和彼此?

2.1 节点发现的三种方式

Sentinel 节点发现机制

1. 配置文件指定主库地址
sentinel monitor mymaster 192.168.1.100 6379 2

2. 向主库发送 INFO replication
获取所有从库信息

3. 订阅主库的 __sentinel__:hello 频道
发现其他 Sentinel

第一步:每个 Sentinel 启动时,从配置文件读取要监控的主库信息。

# sentinel.conf
sentinel monitor mymaster 192.168.1.100 6379 2
#              ↑ 名称    ↑ 主库 IP      ↑ 端口  ↑ quorum(法定人数)

第二步:Sentinel 连接到主库后,每 10 秒发送一次 INFO replication,获取从库列表并自动发现和监控所有从库。

第三步:Sentinel 每秒向主库和从库的 __sentinel__:hello 频道发布一条消息(包含自己的 IP、端口、runid),同时也订阅这个频道。这样,所有监控同一个主库的 Sentinel 就能彼此发现了。

2.2 三个定时任务

Sentinel 有三个核心定时任务,构成了监控的基础:

任务 频率 目标 作用
PING 每秒 主库、从库、其他 Sentinel 心跳检测,判断节点是否存活
INFO 每 10 秒 主库、从库 获取拓扑信息(从库列表、主库状态)
Pub/Sub 每 2 秒 主库的 __sentinel__:hello 发布自身信息,发现其他 Sentinel

三、主观下线(SDOWN)与客观下线(ODOWN):Sentinel 最核心的判断逻辑

3.1 为什么需要两步判断?

这是 Sentinel 设计中最精妙的地方。考虑这个场景:

Sentinel-1 和主库之间的网络断了,但主库本身还活着,
Sentinel-2 和 Sentinel-3 都能正常连接主库。

如果 Sentinel-1 单方面判定主库死了,触发故障转移,
就会出现两个主库——脑裂(Split-Brain)。

解决方案:不能只听一个人说"主库死了",需要多数派确认。

Sentinel PING 主库

响应超时?
(down-after-milliseconds)

标记为主观下线 SDOWN
(Subjective Down)

主库正常

向其他 Sentinel 询问
SENTINEL is-master-down-by-addr

同意票数 ≥ quorum ?

标记为客观下线 ODOWN
(Objectively Down)

保持 SDOWN
不触发故障转移

启动故障转移流程

3.2 SDOWN(主观下线)

单个 Sentinel 的主观判断。Sentinel 每秒向所有节点发送 PING,如果在 down-after-milliseconds(默认 30 秒)内没有收到有效回复,就认为该节点主观下线

# sentinel.conf
sentinel down-after-milliseconds mymaster 30000
# 30 秒无响应 → SDOWN

关键:这只是一个 Sentinel 的个人意见,不足以触发故障转移。

3.3 ODOWN(客观下线)

多个 Sentinel 达成共识后的客观判断。当某个 Sentinel 将主库标记为 SDOWN 后,它会通过 SENTINEL is-master-down-by-addr 命令询问其他 Sentinel 的意见。

同意票数 ≥ quorum 时,该 Sentinel 将主库标记为 ODOWN,并开始故障转移

# quorum 的含义
sentinel monitor mymaster 192.168.1.100 6379 2
#                                            ↑
#         至少需要 2 个 Sentinel 同意才能判定 ODOWN

3.4 quorum 怎么设?

这是 Sentinel 配置中最容易搞错的参数:

Sentinel 总数 推荐 quorum 容错能力
3 2 允许 1 个 Sentinel 挂掉
5 3 允许 2 个 Sentinel 挂掉
7 4 允许 3 个 Sentinel 挂掉

规律quorum = N/2 + 1(过半数原则)。设置为 Sentinel 总数的一半加一。

⚠️ ODOWN 判定用 quorum,Leader 选举用 majority(过半数),两者可能不同! quorum 设小了会导致误判(网络抖动就触发故障转移),设大了会导致故障转移无法触发(宕机了但票数不够)。


四、哨兵集群的领导者选举(Leader Election)

4.1 为什么需要选举?

ODOWN 判定通过后,所有 Sentinel 都知道主库挂了。但只能有一个 Sentinel 来执行故障转移,否则会出现多个 Sentinel 同时选不同从库当主库的混乱局面。

4.2 Raft 协议的简化版

Sentinel 的选举逻辑参考了 Raft 算法,但做了大幅简化:

Sentinel-C Sentinel-B Sentinel-A (候选者) Sentinel-C Sentinel-B Sentinel-A (候选者) 主库被标记为 ODOWN 给自己投一票 SENTINEL is-master-down-by-addr (请求投票,附带自己的 runid) SENTINEL is-master-down-by-addr (请求投票) 检查:我还没投过票? A 的纪元 > 我的纪元? 同意投票 检查:我还没投过票? A 的纪元 > 我的纪元? 同意投票 获得 3 票 ≥ majority(2) 成为 Leader 开始执行故障转移

选举规则

  1. 每个 Sentinel 发现 ODOWN 后,会等待一段时间(随机优先级 + 延迟),然后向其他 Sentinel 请求投票
  2. 每个 Sentinel 在每个纪元(epoch)中只能投一票,先到先得
  3. 获得 ≥ majority(N/2 + 1) 票的 Sentinel 成为 Leader
  4. 如果没有人获得多数票,等待 2 倍故障转移超时后重新选举

注意:这里的 majorityquorum 可能不同。假设 5 个 Sentinel,quorum = 3,但 majority = 3,实际上是一样的。但如果 quorum = 2(不推荐),ODOWN 只需 2 票,但 Leader 仍需要 3 票。


五、自动故障转移(Failover):新主库的诞生

5.1 如何选新主库?

Leader Sentinel 选新主库不是随机的,有一套完整的评分逻辑:

获取所有从库列表

过滤:SDOWN / ODOWN
的从库排除

过滤:断连超过
down-after-milliseconds × 10
的从库排除

对剩余从库打分排序

第一优先级:
replica-priority 最小

第二优先级:
复制偏移量(offset)最大
即数据最完整

第三优先级:
runid 字典序最小

选出最优从库

从库优先级(replica-priority):可以手动配置,数值越小越优先。

# redis.conf(在从库上配置)
replica-priority 10    # 默认 100,越小越优先
replica-priority 0     # 永远不会被选为主库(纯备机)

5.2 故障转移的完整步骤

Slave_Old 客户端 其他从库 选中的从库 Leader Sentinel Slave_Old 客户端 其他从库 选中的从库 Leader Sentinel 如果旧主库恢复上线 1. 从从库列表中 选出最佳候选者 2. SLAVEOF NO ONE (升级为主库) 执行命令,变为主库 3. SLAVEOF <新主库 IP> <端口> (其他从库指向新主库) 指向新主库,开始全量/增量复制 4. 更新内部状态 将旧主库标记为新从库 5. SLAVEOF <新主库 IP> <端口> (旧主库降级为从库) 6. 通过 Pub/Sub 通知客户端 主库地址变更(+switch-master 频道)

5.3 客户端如何感知主库切换?

客户端通过订阅 Sentinel 的 +switch-master 频道,或定期向 Sentinel 查询主库地址来自动切换:

# 查询当前主库地址
redis-cli -h sentinel-host -p 26379 SENTINEL get-master-addr-by-name mymaster
# 返回:["192.168.1.101", "6379"]  ← 新主库地址

# 订阅切换事件
redis-cli -h sentinel-host -p 26379 SUBSCRIBE +switch-master

Java 客户端(如 Lettuce / Redisson)通常内置了 Sentinel 感知机制,能自动发现主库切换并更新连接池,开发者基本无感。


六、实战部署:从零搭建 Sentinel 集群

6.1 架构拓扑

                ┌─────────────────────────┐
                │     Sentinel 集群         │
                │  Sentinel-1  : 26379     │
                │  Sentinel-2  : 26380     │
                │  Sentinel-3  : 26381     │
                └──────────┬──────────────┘
                           │ 监控
                           ↓
        ┌──────────────────┴──────────────────┐
        │                                      │
   ┌────▼─────┐                          ┌────▼─────┐
   │  Master  │◄─────── 复制 ──────────►│ Slave-1  │
   │ :6379    │                          │ :6380    │
   └──────────┘                          └──────────┘
                                               │
                                        ┌──────▼─────┐
                                        │  Slave-2   │
                                        │  :6381     │
                                        └────────────┘

6.2 部署步骤

第一步:准备 Redis 实例

# 主库配置(redis-6379.conf)
port 6379
bind 0.0.0.0
requirepass "your_redis_password"
masterauth "your_redis_password"    # 主库故障转移后变为从库时需要

# 从库1配置(redis-6380.conf)
port 6380
bind 0.0.0.0
replicaof 192.168.1.100 6379
masterauth "your_redis_password"
replica-read-only yes

# 从库2配置(redis-6381.conf)
port 6381
bind 0.0.0.0
replicaof 192.168.1.100 6379
masterauth "your_redis_password"
replica-read-only yes

第二步:准备 Sentinel 配置

# 每个 Sentinel 的配置完全一样(sentinel.conf)
port 26379                                              # Sentinel 端口
sentinel monitor mymaster 192.168.1.100 6379 2         # 监控主库,quorum=2
sentinel auth-pass mymaster your_redis_password        # 主库密码
sentinel down-after-milliseconds mymaster 30000        # 30秒超时
sentinel failover-timeout mymaster 180000              # 故障转移超时 3 分钟
sentinel parallel-syncs mymaster 1                      # 一次只让 1 个从库同步

第三步:启动

# 启动 Sentinel
redis-sentinel /path/to/sentinel.conf

# 或
redis-server /path/to/sentinel.conf --sentinel &

# 验证
redis-cli -p 26379 SENTINEL master mymaster
redis-cli -p 26379 SENTINEL slaves mymaster
redis-cli -p 26379 SENTINEL sentinels mymaster

6.3 Sentinel 配置参数详解

参数 默认值 建议值 说明
sentinel monitor mymaster IP port 2 监控配置,quorum 见上文分析
sentinel down-after-milliseconds 30000 5000-30000 心跳超时时间(毫秒),设置过小会导致网络抖动误判,过大则故障发现延迟增加
sentinel failover-timeout 180000 60000-180000 故障转移的全局超时(毫秒),包括选新主库、数据同步、切换等
sentinel parallel-syncs 1 1-2 故障转移后,允许多少个从库同时向新主库发起全量同步。设 1 最安全
sentinel deny-scripts-reconfig yes yes 禁止 SENTINEL SET 动态修改脚本路径(安全考虑)

七、常见坑点与排查指南

7.1 坑 1:脑裂(Split-Brain)

场景:Sentinel 集群与主库之间网络中断,Sentinel 判定主库 ODOWN 并选出新主库;但旧主库其实还活着,客户端还在往旧主库写数据。此时两个主库并存——脑裂。

客户端A → 旧主库(写入数据A)  ← 这部分数据在故障恢复后会丢失
客户端B → 新主库(写入数据B)

解决方案

# redis.conf(主库配置)
min-replicas-to-write 1          # 至少要有 1 个从库在线
min-replicas-max-lag 10          # 从库延迟不超过 10 秒

# 含义:如果主库发现从库数量不足或延迟过大,拒绝写入
# 这样脑裂时旧主库无法写入,避免数据分歧

7.2 坑 2:Sentinel 无法达成一致

症状+odown 日志出现但没有 +failover。可能原因:

原因 排查方法
quorum 设置过大 SENTINEL master mymaster 检查 quorum
Sentinel 之间网络不通 SENTINEL sentinels mymaster 检查是否互相发现
Sentinel 时钟不同步 确认 NTP 服务正常
被监控的主库设置了密码但 Sentinel 没配置 sentinel auth-pass 检查

7.3 坑 3:故障转移后从库全部全量同步

场景:新主库选出来后,所有从库同时发起全量复制,新主库内存暴涨 OOM。

解决方案

sentinel parallel-syncs 1  # 一次只让 1 个从库同步,排队

7.4 坑 4:Sentinel 本身的单点问题

关键原则:Sentinel 集群必须部署在不同的物理机或不同的故障域上。如果 3 个 Sentinel 全在同一台机器上,机器挂了全部不可用。

✅ 正确部署:
Sentinel-1: 物理机A
Sentinel-2: 物理机B  
Sentinel-3: 物理机C

❌ 错误部署:
Sentinel-1~3 全在物理机A 上(或同一台虚拟机的 3 个容器中)

7.5 故障排查命令速查

# 查看 Sentinel 整体状态
SENTINEL master mymaster

# 关键字段解读
# flags=master                        ← 当前状态
# num-slaves=2                        ← 从库数量
# num-other-sentinels=2               ← 其他 Sentinel 数量
# quorum=2                            ← quorum 配置
# failover-timeout=180000
# parallel-syncs=1

# 查看主库的从库列表
SENTINEL slaves mymaster

# 查看所有 Sentinel 实例
SENTINEL sentinels mymaster

# 手动触发故障转移(测试用)
SENTINEL failover mymaster

# 查看当前纪元(epoch)
SENTINEL ckquorum mymaster

# 重置 Sentinel 状态(清理过时信息)
SENTINEL reset mymaster

八、总结

本文的核心知识图谱:

Sentinel 高可用

监控层

判断层

执行层

PING 心跳(每秒)

INFO 拓扑发现(每10秒)

Pub/Sub 互相发现(每2秒)

SDOWN → 个人判断

ODOWN → 多数派共识(quorum)

Leader 选举 → Raft 简化版(majority)

选主:replica-priority > offset > runid

切换:SLAVEOF NO ONE

通知:+switch-master

三个必须记住的数字

参数 作用 常见错误
quorum ODOWN 判定所需票数 设太小→误判,设太大→判不了
down-after-milliseconds 心跳超时 设太小→网络抖动就触发故障转移
parallel-syncs 故障转移后并发同步数 设太大→新主库 OOM

如有疑问或指正,欢迎在评论区交流。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐