Redis Cluster 故障转移细节与 cluster-node-timeout 调参

举报
yd_216253085 发表于 2026/11/12 17:08:11 2026/11/12
【摘要】 从 PFAIL 到选举晋升,讲透故障转移机制与超时参数权衡

Cluster 不需要外挂哨兵:故障检测、投票、选主全部由节点自己完成。这套内建机制决定了两件事——故障恢复有多快、什么情况下会误判切换。而这两件事的旋钮都集中在 cluster-node-timeout 一个参数上。本文基于 Redis 7,把从 PFAIL 到新主接管的全过程拆开讲,再给出调参依据。

一、故障检测:PFAIL 与 FAIL 两级状态

节点之间通过 gossip 每秒互发 PING/PONG。当一个节点超过 cluster-node-timeout 没有收到某个节点的 PONG,就在自己的视图里把它标记为 PFAIL(疑似下线),这是主观判断。PFAIL 状态会随心跳包携带的 gossip 消息扩散;当超过半数的主节点都报告了 PFAIL,该状态升级为 FAIL(确认下线),并被广播给全集群。两个细节值得注意:只有主节点有投票权,副本的判断不参与计数;判断「过半数主节点」的分母是集群内持有槽的主节点总数。

PING 的发送节奏与超时的关系值得拆开。集群节点每秒随机挑选部分已知节点发送 PING,对长时间没有通信的节点会优先发送,所以"多久没收到 PONG"并不是严格的固定周期采样。cluster-node-timeout 的判定条件是该节点在超时窗口内完全没有有效的 PONG 回复,包括因为对方阻塞而延迟到达的回复。这也是为什么主线程阻塞会造成误判:节点并没有宕机,只是没能在窗口内应答。

观测这两级状态有两种粒度。粗粒度看 CLUSTER INFO 的四个槽计数:

redis-cli -p 6379 cluster info | grep -E "cluster_state|slots_pfail|slots_fail"
# cluster_state:ok
# cluster_slots_pfail:0
# cluster_slots_fail:0

cluster_slots_pfail 不为零但 cluster_state 还是 ok,说明有节点被部分主节点怀疑但尚未达成多数,此时应当立刻查该节点的日志与主线程耗时,而不是等它升级为 FAIL。细粒度则看 CLUSTER NODES 里目标节点的 flags 列,fail? 表示当前节点自己认为它疑似下线,fail 表示已经达成多数确认。这两种标记的差别正是两级机制在输出上的体现。

二、从节点选举与晋升

主节点进入 FAIL 后,它的副本开始竞争晋升,流程如下:

  1. 每个候选副本按复制偏移量排名(数据越新排名越靠前),错峰发起选举:约在 500 毫秒加排名序号乘 500 毫秒的基础上再加随机抖动后,把 currentEpoch 加一并向所有主节点请求投票;
  2. 每个主节点在同一纪元内只能投一票,通常投给最先到达(即数据最新的)候选者;
  3. 拿到多数主节点选票的副本晋升为新主,向全集群广播携带新纪元的 PONG,接管原主节点的全部槽位。

数据太旧的副本不允许参选。判定标准由 cluster-replica-validity-factor(默认 10)控制:副本与主断连的时长超过 cluster-node-timeout 乘以该系数,就放弃参选;设为 0 表示不做数据新旧检查,仅建议测试环境使用。

三、cluster-node-timeout 怎么调

这个参数同时影响检测速度和误判概率。一次故障转移的总不可用时间大致等于:发现时间(约一个 node-timeout)加上选举与拓扑传播的一两秒。默认值 15000 毫秒意味着主节点宕机后业务不可写 15 秒上下。

调小的收益是恢复快,但两个误判来源会放大:一是主线程阻塞——KEYS 扫描、大 Lua、大 key 删除都可能让主线程无响应数秒,若阻塞超过 node-timeout,其他节点会把它判成 PFAIL 并触发切换;二是网络抖动,跨可用区部署或虚拟化宿主繁忙时 PONG 延迟会周期性尖刺。调大的代价则是真故障时恢复更慢。实践建议:命令使用受控、网络质量好的集群可以压到 5 至 10 秒;存在慢命令风险、宿主争抢严重的集群保持 15 秒,先治理慢命令再考虑下调。

给出一个可以直接照做的判断方法:先量化实际抖动分布,再决定值。用 redis-cli --latency 对每个节点采样几分钟,或者直接看 LATENCY HISTORY 里的 command 事件峰值:

redis-cli -p 6379 --latency
# min: 0, max: 6, avg: 0.31 (1024 samples)

redis-cli -p 6379 latency history command
# 1) 1) (integer) 1761350400
#    2) (integer) 12
# 2) 1) (integer) 1761350460
#    2) (integer) 183

第二段输出显示某分钟出现了 183 ms 的事件峰值。把观察窗口内的最大值乘以一个安全系数(经验上取 3 到 5),得到的就是 node-timeout 的下限。本例 183 ms 乘 5 约 1 秒,远小于 5 秒,说明 5 秒是安全的;如果最大峰值为 3 秒,就该保留 15 秒默认值,先去查那 3 秒是什么造成的。

这个方法的逻辑是:node-timeout 必须大于"正常情况下的最大响应延迟",否则节点会因为自己忙不过来而被别人判死。反过来,它也不必远大于故障检测需要的灵敏度,因为真正的宕机是"完全无响应",与"响应慢"在行为上有本质区别。

四、相关参数与主动切换

还有几个参数与故障转移联动:cluster-require-full-coverage yes(默认)表示任一槽失主就整体拒绝服务,按业务权衡可设为 no;cluster-allow-reads-when-down no 控制从库在主 FAIL 后是否继续提供旧数据读;cluster-migration-barrier 1 要求每个主节点至少保留的副本数,低于该值时集群会把副本从富余的主节点迁移过来补位。

计划内切换不要等真实故障演练,直接在目标从节点上执行:

redis-cli -h <从节点> -p 6379 cluster failover        # 优雅切换:先补齐复制偏移再晋升,不丢数据
redis-cli -h <从节点> -p 6379 cluster failover force  # 跳过偏移确认,允许少量丢失
redis-cli -h <从节点> -p 6379 cluster failover takeover

takeover 绕过多数派投票直接晋升,仅用于多数派失联的极端恢复场景——它可能产生与多数派观点冲突的新主,普通运维不要使用。

常见坑 / 最佳实践

  1. 先治慢命令再压 node-timeout。 主线程被 KEYS 或大脚本阻塞超过阈值就会触发无谓切换,切换本身又带来断连与数据窗口,比慢命令更伤。
  2. 不要把 validity-factor 设为 0 上生产。 这等于允许数据严重落后的副本当选,切换后相当于回退时间线。
  3. 每主至少一个副本,重要分片两个。 只有单副本的主节点 FAIL 后,该分片在恢复前没有冗余,下一次故障就是槽失主。
  4. 故障演练做两种方式。 用 CLUSTER FAILOVER 验证计划内切换,用 kill 主进程验证真实故障转移,分别记录恢复耗时,参数才有调整依据。
  5. require-full-coverage 的取舍要写进文档。 设为 no 换取部分槽继续服务,但业务必须能容忍局部数据不可用,这个决定不该由值班临时做。

小结

  • 故障检测是 PFAIL(单节点主观)到 FAIL(多数主节点确认)的两级升级,只有持有槽的主节点有投票权。
  • 从节点按复制偏移排名错峰发起选举,获多数票者晋升;数据断连过久的副本被 validity-factor 挡在门外。
  • cluster-node-timeout 同时决定检测速度与误判概率,默认 15 秒;下调的前提是慢命令与网络抖动已经受控。
  • CLUSTER FAILOVER 提供优雅、force、takeover 三档,计划内切换用默认档保证零丢失,takeover 仅限极端恢复。
  • coverage、migration-barrier、副本数量共同构成故障转移后的集群形态,参数变更要演练验证。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。