Redis 集群主节点掉了,你预期 15 秒内副本会接管。5 分钟过去,集群仍在 fail 状态,CLUSTER NODES 显示主为 fail、副本仍是 slave,没升主,应用对那个 slot 范围的所有 key 都在报错。这是故障切换卡住。原因几乎一定是 quorum 丢失、Sentinel 之间网络分区、副本优先级配置不对、或副本落后太多不够格升主。修复方向:查 quorum、修通 Sentinel 之间的网络、必要时手动 takeover。
常见原因
按踩坑频率排序。
1. Sentinel quorum 不够
你部署了 3 个 Sentinel,但只有 1 个看到主节点挂了。quorum 要求多数同意,所以根本没开始切换。
怎么判断:在每个 Sentinel 上跑 SENTINEL MASTERS。只有一个 Sentinel 报主挂,就是 quorum 凑不齐。
2. Sentinel 跟副本之间网络断了
Sentinel 之间能互通但看不到副本。Sentinel 投票决定开始 failover,但升不了主。
怎么判断:在每个 Sentinel 主机上跑 redis-cli -h <replica> PING。失败就是分区。
3. 副本优先级设成了 0
replica-priority 0 标记副本不能被升主。所有副本都是 0 时,根本没法 failover。
怎么判断:每个副本上 CONFIG GET replica-priority。0 = 不参选。
4. 副本落后主节点太多
默认 cluster-replica-validity-factor 是 10。主每秒 ping 一次,所以断 10 秒副本就不够格。长时间断 = 没有合格副本。
怎么判断:每个副本上 INFO replication。master_link_down_since_seconds 超过 100 = 副本数据太旧。
5. 集群开了 cluster-require-full-coverage
设成 yes 时,任意 slot 范围丢失都会让整个集群拒绝写入。Redis 6+ 默认是 yes(更安全)。
怎么判断:CONFIG GET cluster-require-full-coverage。yes + 某 slot 挂 = 集群拒写。
6. 手动 failover 留下了不一致状态
之前有人跑过 CLUSTER FAILOVER TAKEOVER,把集群弄成了不一致状态。后续的自动 failover 拒绝再动。
怎么判断:最近运维日志里有手动 cluster 命令。检查 CLUSTER NODES 里有没有异常 flag。
动手前先确认
- 确认集群是真的卡住,不是只是慢。默认 failover 要 15 到 30 秒。
- 找出受影响的 slot 范围和挂掉的主。
- 看应用影响:哪些 key 前缀打不通。
- 改任何东西之前,先把每个节点的当前状态记录下来。
- 留个回滚方案:动集群之前先做快照。
需要收集的信息
- 至少 3 个不同节点的
CLUSTER NODES输出。 - 每个副本的
INFO replication。 - 每个 Sentinel 的
SENTINEL MASTERS和SENTINEL SLAVES <name>。 - 每个 Sentinel 到每个节点的网络连通性测试。
- 最近的运维日志,特别是手动跑过的命令。
分步修复
Step 1:验证 quorum
# 在每个 Sentinel 上
redis-cli -p 26379 SENTINEL MASTERS
# 如果 3 个 Sentinel 只有 1 个看到主挂,
# 另外 2 个看不到 -> quorum 凑不齐
quorum 凑不齐时先恢复网络。不要降 quorum 阈值——会引入脑裂风险。
Step 2:修通 Sentinel 跟副本之间的网络
# 在每个 Sentinel 主机上测连通性
for replica in replica1 replica2 replica3; do
redis-cli -h $replica -p 6379 PING
done
# 检查防火墙和安全组
iptables -L | grep 6379
# AWS:检查安全组放通 Sentinel 子网
# 被挡了就放通
ufw allow from <sentinel-subnet> to any port 6379
Step 3:检查和修正副本优先级
# 每个副本上
redis-cli -h replica1 CONFIG GET replica-priority
# 恢复参选资格
redis-cli -h replica1 CONFIG SET replica-priority 100
# 持久化到配置文件
redis-cli -h replica1 CONFIG REWRITE
优先级越低越优先选。100 是默认值。
Step 4:检查副本数据新鲜度
# 每个副本上
redis-cli -h replica1 INFO replication
# 关注
# master_link_status: up | down
# master_link_down_since_seconds
# master_last_io_seconds_ago
副本断的时间超过 cluster-replica-validity-factor * 10 秒就不参选。要么调小 validity factor,要么等副本重新同步。
redis-cli CONFIG SET cluster-replica-validity-factor 20
Step 5:手动强制 takeover(最后手段)
自动 failover 死活不走、集群卡死:
# 在选好的副本上
redis-cli -h replica1 CLUSTER FAILOVER
# 选项:
# CLUSTER FAILOVER -- 标准,需要主同意
# CLUSTER FAILOVER FORCE -- 副本不同步也强切
# CLUSTER FAILOVER TAKEOVER -- 无共识,最后手段
TAKEOVER 跳过共识检查。只在下面情况用:
- 老主已确认死亡
- 其他副本已确认不可达
- 接受未复制写入的数据丢失
Step 6:升主后验证集群状态
redis-cli CLUSTER INFO
# 期望:
# cluster_state: ok
# cluster_slots_assigned: 16384
# cluster_slots_ok: 16384
# cluster_known_nodes: <预期数>
# 必要时再重置所有 slot 状态
redis-cli CLUSTER NODES
Step 7:调整 cluster-require-full-coverage 做优雅降级
# 允许集群在某个 slot 挂时仍服务其他 slot
redis-cli CONFIG SET cluster-require-full-coverage no
redis-cli CONFIG REWRITE
权衡:未覆盖的 slot 写入会失败,但集群其余部分能继续对外服务。
验证
- 每个节点
CLUSTER INFO都显示cluster_state: ok。 SENTINEL MASTERS显示一个健康主,没有down-after-milliseconds状态。- 应用能读写之前受影响 slot 范围的 key。
- 副本延迟(
master_repl_offset差值)回到 100 KB 以内。
长期预防
- Sentinel 用奇数(3 或 5),分布在至少 3 个可用区。
- 给每个副本显式设
replica-priority,除非有意,否则不要留 0。 - 监控副本延迟,超过 10 秒持续 1 分钟就告警。
- 每月跑一次 failover 演练,确认自动升主 30 秒内完成。
- 把
cluster-require-full-coverage的选择写文档:读可用性优先用 no,写一致性优先用 yes。
容易踩的坑
- 把 Sentinel quorum 降到少数,来”修”failover——保证会脑裂。
- 没确认老主已死就跑
FAILOVER TAKEOVER——数据丢。 - 改完优先级忘了
CONFIG REWRITE——重启后又变回去。 - 所有 Sentinel 放一个 AZ——单 AZ 故障全集群跟着挂。
FAQ
为什么集群健康时 failover 也要 30 秒? 默认 down-after-milliseconds 是 30000。可以降到 5000 到 10000 做更快检测,代价是误判增多。
该不该调小 replica-validity-factor? 只有在能接受升一个落后几十秒的副本时才能。缓存场景没问题;会话存储宁可等。
能完全避免脑裂吗? 不能,但能尽量减小:奇数 Sentinel、多 AZ 部署、保守 quorum、除非确认老主已死否则不跑手动 TAKEOVER。