Redis 集群掉了一个主节点,你预期 15 到 30 秒内会有副本接管。几分钟过去集群还是坏的:CLUSTER INFO 显示 cluster_state: fail,CLUSTER NODES 里挂掉的主被标成 master,fail、它的副本还是 slave,那个主负责的 hash slot 范围里每个 key 都返回 CLUSTERDOWN The cluster is down 或者 MOVED/TRYAGAIN。这就是故障切换卡住了。
最快的修法: 先确认多数主节点还活着并且能互相通信(3 主集群至少要 2 个可达),再确认那个存活副本的数据是新的,然后在该副本上跑 CLUSTER FAILOVER FORCE。如果老主已经确认死亡、而且你已经丢了主节点 quorum,那 CLUSTER FAILOVER TAKEOVER 才是最后手段。本文剩下的部分讲的是在你动用 TAKEOVER 之前怎么找到真正的原因——因为在这里走错捷径会导致脑裂和数据丢失。
本文针对的是真正的 Redis Cluster(cluster-enabled yes),不是 Sentinel。这两套是不同的产品、修法也不同——下一节帮你五秒钟分清自己用的是哪一套。
先分清楚 Cluster 和 Sentinel
Redis 有两套完全不同的高可用方案,修法也不一样:
- Redis Cluster(本文)把数据分片到多个主节点,靠 gossip 协议自己处理故障切换。没有 Sentinel 进程。由副本发起选举,由多数主节点投票。诊断命令是
CLUSTER INFO和CLUSTER NODES。 - Redis Sentinel 是一组单独的 sentinel 进程,监控一个不分片的主从组,靠
replica-priority和 Sentinel quorum 工作。诊断命令是SENTINEL MASTERS和SENTINEL SLAVES。
如果你跑 redis-cli -p 26379 SENTINEL MASTERS 能拿到响应,那你用的是 Sentinel 而不是 Cluster,相关配置是 replica-priority 和每个主的 quorum。下面全部假设你用的是真正的 Redis Cluster(节点以 cluster-enabled yes 启动)。再强调一次:replica-priority 只有 Sentinel 会读,在 Cluster 模式下被忽略——设了它对集群故障切换没有任何作用。
集群故障切换到底怎么走
搞清楚机制就知道它会卡在哪。截至 2026 年 6 月(Redis 7.x 和 8.x),流程是:
- 某节点不再应答 ping。超过
cluster-node-timeout(默认15000毫秒)后,发现它的节点把它标成PFAIL(疑似故障)并 gossip 出去。 - 当多数主节点都把同一个节点报成
PFAIL,它才升级为FAIL。到这一步才可能开始切换。 - 挂掉主的每个合格副本算一个很短的选举延迟(
500ms + random(0-500ms) + rank * 1000ms,rank 0 是复制偏移量最高、也就是数据最新的副本),然后向各主请求投票。那个固定的 500 毫秒是为了给FAIL状态留出传播时间,免得在多数主还没认这是FAIL时就发起投票;随机部分让各副本错开;rank 保证最新的副本先开口。 - 当某副本在同一 config epoch 内拿到多数主的票就胜出。3 主集群要 2 票,5 主集群要 3 票。胜出者拿到一个更高的新
configEpoch,接管挂掉主的 slot。
任何破坏第 2 步(没有多数主看到 FAIL)或第 4 步(没有多数主能投票)的情况,都会让你卡在 fail 状态,副本永远是 slave 升不了主。准确的规则见 Redis Cluster 规范。
常见原因
按造成卡住的频率排序。
| # | 原因 | 一句话排查 | FAIL 标记有没有? |
|---|---|---|---|
| 1 | 丢了主节点 quorum(太多主挂了或被分区) | CLUSTER INFO 里 cluster_known_nodes 在,但可达的主更少 | 没有 |
| 2 | 主卡在 fail?(PFAIL),没升到 fail | CLUSTER NODES 显示 fail? 而不是 fail | 没有 |
| 3 | 挂掉的主压根没有副本 | CLUSTER NODES 里没有指向它的 slave 行 | 不适用 |
| 4 | 副本落后太多(超过有效期窗口) | INFO replication 里链路断的时间超过有效期窗口 | 有 |
| 5 | 副本被 cluster-replica-no-failover yes 挡住 | CONFIG GET cluster-replica-no-failover 返回 yes | 有 |
| 6 | 新副本还没被多数主认识 | 在每个主上跑 CLUSTER NODES 都没列出这个副本 | 有 |
| 7 | cluster-require-full-coverage yes 掩盖了恢复(集群拒绝所有写) | CONFIG GET cluster-require-full-coverage 返回 yes | 有 |
1. 丢了主节点 quorum
这是最常见的真实原因。集群故障切换需要多数主节点,不是看节点总数。加副本永远改变不了这个算法。3 主集群里如果有 2 个主挂了或被分区,剩下那 1 个达不到多数,任何副本都升不了主。
怎么判断: 跑 CLUSTER INFO。如果 cluster_state:fail,而从一个健康节点出发 3 个主里只能连到 1 个,你就是丢了 quorum。先恢复到其他主的连通性;不要想着对每个分片同时 TAKEOVER 来”修”这个问题。
2. 主卡在 fail?
被标成 fail? 的节点是被怀疑了(PFAIL),但多数主还没确认它为 fail。在升到 fail 之前不会开始选举。这通常发生在只有一个主能看到故障时(不对称分区)。
怎么判断: CLUSTER NODES 显示 master,fail? 而不是 master,fail。查一下为什么其他主仍觉得它可达(单向防火墙规则、安全组、集群总线端口上的 MTU/丢包)。
3. 挂掉的主没有副本
不存在的东西没法升主。如果故障的主一个副本都没有,集群就直接丢掉那些 slot。
怎么判断: 看 CLUSTER NODES 里有没有 slave <挂掉主的 id> 行。没有就是没有候选。
4. 副本落后太多
副本只有在数据足够新时才合格。有效期窗口是 cluster-node-timeout * cluster-replica-validity-factor / 1000 秒。用默认值(cluster-node-timeout 15000、cluster-replica-validity-factor 10)就是 15 * 10 = 150 秒。链路断的时间超过这个的副本不会自己升主。
怎么判断: 在副本上跑 INFO replication。看 master_link_status、master_link_down_since_seconds、master_last_io_seconds_ago。如果断线时间超过有效期窗口,它就被判为不合格。
5. 副本被显式禁止切换
cluster-replica-no-failover yes 让副本永远不自动升主。如果挂掉主的所有副本都设了这个,自动切换就发生不了。
怎么判断: 在每个副本上 CONFIG GET cluster-replica-no-failover。yes = 被挡。(再说一遍:replica-priority 是 Sentinel 的设置,在这里没作用。)
6. 新副本还没被各主认识
副本只有在被多数主认作副本之后才能当选。在故障前一刻才加进来的节点,可能还没传播开。
怎么判断: 在每个主上跑 CLUSTER NODES(或 CLUSTER REPLICAS <主-id>),确认这个副本出现了,再去依赖它。
7. cluster-require-full-coverage 掩盖了恢复
设成 yes(默认)时,只要有任何一个 slot 没被覆盖,整个集群就拒绝命令,于是连健康的分片看起来都像挂了。它本身不挡切换,但会把局部故障看成全面故障,逼你仓促 TAKEOVER。
怎么判断: CONFIG GET cluster-require-full-coverage。yes + 某 slot 范围没覆盖 = 整个集群返回 CLUSTERDOWN。
动手前先确认
- 确认是真卡住,不是只是慢。正常的集群切换大约要
cluster-node-timeout再加几秒(默认约 15 到 20 秒,最多 30 秒)。 - 找出哪个(些)主是
fail,以及受影响的 hash slot 范围(一共 16384 个)。 - 记下应用里哪些 key 前缀打不通。
- 改任何东西之前先抓状态:至少 3 个节点的
CLUSTER NODES、CLUSTER INFO,以及每个副本的INFO replication。 - 留好回滚方案:动集群之前先做快照(
BGSAVE或拷一份 RDB/AOF)。
分步修复
Step 1:确认 FAIL 标记和主节点 quorum
# 在一个健康节点上
redis-cli -h <healthy-node> -p 6379 CLUSTER INFO
redis-cli -h <healthy-node> -p 6379 CLUSTER NODES
在 CLUSTER NODES 里,挂掉的主应该显示 master,fail。如果显示 master,fail?,说明集群还没确认这次故障(原因 #2),问题出在主与主之间的连通性,而不是副本。数一下你到底能连上几个主:在任何切换能完成之前,必须有多数主活着。
Step 2:恢复集群总线和主与主之间的连通性
集群节点用两个端口通信:客户端端口(6379)和集群总线端口,后者是客户端端口加 10000(16379),除非用 cluster-port 覆盖。每对节点之间这两个端口都必须开放。
# 从每个节点到其他每个节点,测两个端口的可达性
redis-cli -h <other-master> -p 6379 PING
nc -vz <other-master> 16379 # 集群总线端口
# 检查防火墙规则
iptables -L -n | grep -E '6379|16379'
# AWS:确认安全组在所有节点子网之间放通 6379 和 16379
# 例:总线端口被挡就放通
ufw allow from <cluster-subnet> to any port 16379 proto tcp
总线端口被挡是主卡在 fail?、永远到不了 fail 的典型原因:客户端能用,但 gossip 和投票走不通。
Step 3:确认存在一个合格副本
# 在某个主上,列出挂掉主的副本
redis-cli -h <master> -p 6379 CLUSTER REPLICAS <dead-master-node-id>
# 在候选副本上,检查新鲜度和链路状态
redis-cli -h <replica> -p 6379 INFO replication
# 关注:
# role:slave
# master_link_status:down
# master_link_down_since_seconds
# slave_repl_offset
如果一个副本都没列出来(原因 #3),就没法切到任何东西,只能加节点重建这个分片。如果链路断的时间超过有效期窗口(原因 #4),副本被判为不合格——要么等它重新同步,要么把窗口调大:
# 全集群放宽资格窗口(有效期 = node_timeout * factor / 1000)
redis-cli -h <node> -p 6379 CONFIG SET cluster-replica-validity-factor 20
# 设成 0 完全关闭新鲜度检查(接受一个旧副本)
Step 4:解封那些主动退出切换的副本
# 在挂掉主的每个副本上检查
redis-cli -h <replica> -p 6379 CONFIG GET cluster-replica-no-failover
# 如果返回 "yes",重新打开自动升主
redis-cli -h <replica> -p 6379 CONFIG SET cluster-replica-no-failover no
redis-cli -h <replica> -p 6379 CONFIG REWRITE
集群模式下别去动 replica-priority——它只有 Sentinel 会读,在这里被忽略。
Step 5:手动强制故障切换
如果主已死、主与主之间能互通、又有一个数据新的副本但还是没升主,就在副本上触发:
# 在选好的副本节点上跑
redis-cli -h <replica> -p 6379 CLUSTER FAILOVER FORCE
三种模式,从保证最弱到最强:
CLUSTER FAILOVER—— 跟老主协调以避免数据丢失。需要老主可达,所以主死了就用不上。CLUSTER FAILOVER FORCE—— 跳过跟(不可达的)主握手,但仍需多数主授权新 epoch。这是主崩溃时的正常选择。CLUSTER FAILOVER TAKEOVER—— 完全跳过集群授权。副本单方面取下一个 config epoch 并接管 slot。只在你已丢主 quorum 且无法恢复时用,因为老主回来会脑裂。
只有下面三条都成立才用 TAKEOVER:
- 老主已确认死亡(不是只是被分区)。
- 你无法在可接受的时间内恢复多数主。
- 你接受丢掉那些还没复制出去的写入。
CLUSTER FAILOVER(不管带不带 FORCE)会立刻返回 OK,但不保证成功:按CLUSTER FAILOVER 官方文档的说法,它只是排了一次切换。只有 TAKEOVER 是同步执行的。如果排了却一直没完成,去副本日志里查:
Manual failover timed out—— 副本等了几秒后放弃了。重新检查主 quorum 和总线端口。Currently unable to failover: Waiting for votes, but majority still not reached—— 副本能连到各主,但拿不到票。这几乎总是主侧连通性问题(主与主之间总线端口被挡),或者这个副本还没被多数主认识(原因 #6)。
Step 6:验证升主
# 在原来的副本上
redis-cli -h <replica> -p 6379 ROLE # 现在应打印 "master"
redis-cli -h <replica> -p 6379 INFO replication # role:master
# 从任意节点
redis-cli -h <node> -p 6379 CLUSTER INFO
# 期望:
# cluster_state:ok
# cluster_slots_assigned:16384
# cluster_slots_ok:16384
redis-cli -h <node> -p 6379 CLUSTER NODES # 升主的节点现在标 "master"
Step 7:决定 cluster-require-full-coverage
redis-cli -h <node> -p 6379 CONFIG GET cluster-require-full-coverage
# 让集群在局部故障时仍服务已覆盖的 slot
redis-cli -h <node> -p 6379 CONFIG SET cluster-require-full-coverage no
redis-cli -h <node> -p 6379 CONFIG REWRITE
权衡:写没被覆盖的 slot 仍会失败,但健康分片能继续服务,而不是整个集群返回 CLUSTERDOWN。读可用性优先选 no,严格写一致性优先选 yes。
怎么确认已经修好了
- 每个节点的
CLUSTER INFO都显示cluster_state:ok和cluster_slots_ok:16384。 CLUSTER NODES里升主的节点是master,老主要么消失、要么变成slave。- 升主节点上
ROLE打印master。 - 应用能读写之前受影响 slot 范围的 key,没有
CLUSTERDOWN/MOVED死循环。 - 老节点回来重新挂成副本后,它上面的
INFO replication显示master_link_status:up,偏移量差距收敛。
长期预防
- 至少部署 3 个主,分布在 3 个可用区,这样掉一个区还能保住主多数。(quorum 看主的数量,所以 3 主能容忍 1 个挂。)
- 给每个主至少配一个副本,最好在不同区,让每个分片都有升主候选。
- 在所有节点之间同时开放客户端端口和集群总线端口(
端口 + 10000);总线端口被挡是主卡在fail?上不去fail的头号原因。 - 有意识地调
cluster-node-timeout。调小(比如 5000 毫秒)检测更快,但网络抖一下就可能误切;默认 15000 毫秒更保守。 - 监控副本延迟和有效期窗口;当副本的
master_link_down_since_seconds接近cluster-node-timeout * cluster-replica-validity-factor / 1000时告警。 - 每月跑一次切换演练:杀一个主,确认自动升主在大约
cluster-node-timeout再加几秒内完成。
容易踩的坑
- 把 Cluster 当 Sentinel 用:调
replica-priority或 Sentinel 的quorum对集群切换毫无作用。 - 老主只是被分区(没死)就跑
CLUSTER FAILOVER TAKEOVER——它回来你就脑裂、写入分叉。 - 忘了集群总线端口(默认
16379)。客户端连得上,但 gossip 和投票走不通,主永远卡在fail?。 CONFIG SET之后忘了CONFIG REWRITE——重启后改动就没了。- 所有主放一个 AZ——单区故障就跌破主多数,谁也切不了。
FAQ
主明明已经死了,为什么副本没自动升主? 最常见的是多数主无法确认这次故障(挂掉的主因为集群总线端口被挡卡在 fail?),或者你丢了主 quorum。自动升主需要多数主先把节点标成 fail、再投票,跟你有多少副本无关。
CLUSTER FAILOVER FORCE 和 TAKEOVER 用哪个? 只要多数主还活着就用 FORCE,它能拿到正经授权的 config epoch。只有在你丢了主 quorum 且无法恢复、并且接受老主回来脑裂的风险时,才用 TAKEOVER。
replica-priority 能控制集群里哪个副本胜出吗? 不能,那是 Sentinel 的设置。在 Redis Cluster 里数据最新的副本(rank 最低、复制偏移量最高)先发起选举;要彻底排除某个副本,就在它上面设 cluster-replica-no-failover yes。
健康的切换应该多久? 大约 cluster-node-timeout(默认 15 秒)让节点到 FAIL,加上不到一秒的选举延迟,所以用默认值大概 15 到 20 秒走完,最多约 30 秒。超过一两分钟才算真卡住。
该不该调小 cluster-replica-validity-factor? 调小会让切换更难(副本要更新才合格);调大或设成 0 让更旧的副本也能升主。缓存场景用更大值或 0 偏向可用性;会话/数据存储保持默认,宁可等重新同步。
副本日志报 Waiting for votes, but majority still not reached,怎么办? 副本在尝试当选,但各主不投票。副本能连到各主,但要么各主无法达成”老主是 FAIL”的共识(主与主之间总线端口被挡,挂掉的节点卡在 fail?),要么这个副本还没被多数主认识(原因 #6)。先修好主与主之间总线端口(默认 16379)的连通性,并确认这个副本在每个主的 CLUSTER NODES 里都出现了,再重试 CLUSTER FAILOVER FORCE。