问题背景
公司使用 Redis Cluster 6.2 作为分布式缓存,6 节点(3 主 + 3 从),部署在 3 台物理服务器上(每台 1 主 1 从),采用默认的 Gossip 协议进行节点间通信和故障检测。Cluster 承载了商品详情缓存、用户 Session 和实时价格缓存,日均请求量约 5 万 QPS。
故障现象
10 月 19 日下午 3:00,部署了 Redis 相关的 Kubernetes 网络策略变更后,业务逐渐出现异常:商品详情页展示的价格与实际价格不一致,部分用户登录状态丢失需要重新登录。
|
|
集群状态 fail,一半的 hash slots 不可用。
|
|
两个不同节点看到完全不同的集群视图——node01 看见 2 主,认为集群失败;node04 看见 3 主,认为集群正常。这就是典型的脑裂(Split-Brain)。
排查过程
第一步:确认 Gossip 通信状态
|
|
节点 j0k1l2(node03 的主节点)被标记为 fail?(疑似下线)。但从另一个子集群看,node03 正常运行且已被提升为独立的主节点。
第二步:分析故障触发时间线
|
|
网络仅中断了约 10 秒(15:02:30–15:02:40),但 Redis Cluster 的 cluster-node-timeout 默认值为 15000ms(15 秒)——网络中断时间 < cluster-node-timeout,理论上不应该触发 failover。
第三步:发现配置不一致
|
|
node03 的 cluster-node-timeout 被手动改成了 5000ms(5 秒)。这 5 秒刚好落在网络中断的 10 秒窗口内——于是 node01 和 node02 在 5 秒后判定 node03 下线,触发 failover。而 node03 同样在 5 秒后判定 node01/02 下线,也自己触发了一轮选举。
第四步:为什么 node03 的 timeout 是 5 秒?
追溯配置历史发现:两周前一名开发在做本地 Redis 测试时,通过 CONFIG SET 临时改了 node03 的 timeout 方便测试自动故障转移性能,但测试结束后忘记改回来。这个"临时"配置被 Redis 持久化到了 redis.conf 中。
解决方案
紧急恢复
|
|
清理不一致数据
脑裂期间两个子集群都接收了写入,需要找出冲突的 key 并以主集群数据为准做覆盖。
根因分析
直接原因:两个节点 cluster-node-timeout 配置不一致(5s vs 15s),网络瞬时中断的 10 秒刚好落在 5 秒的故障检测窗口内却还没到 15 秒,造成两边互相认为对方下线,触发双向 failover 形成脑裂。
深层原因:
CONFIG SET是实时生效但不区分"临时"和"永久"——通过CONFIG REWRITE会持久化- 缺乏 Redis 配置的变更审计和一致性检查机制
cluster-node-timeout这个关键参数没有统一管理
预防措施
- 配置统一管理:所有 Redis 节点的关键参数通过 Ansible/Puppet 统一推送,禁止手动配置修改
- 配置一致性检查:定期巡检所有 Redis 节点的
cluster-node-timeout、cluster-slave-validity-factor等关键参数 - 最小 timeout 经验值:
cluster-node-timeout建议 ≥ 网络最大 RTT × 3,生产环境推荐 15-30 秒 - 脑裂检测脚本:定期从不同节点执行
CLUSTER NODES对比集群视图,不一致立即告警 - 网络变更窗口:任何可能影响 Redis 节点间通信的网络变更,必须在 Redis 低峰窗口执行
总结
Redis Cluster 的脑裂在理论上很少发生——前提是所有节点的关键配置一致。一个被人随手改了 5 秒 timeout 的节点,在网络抖动的瞬间就成了集群分裂的导火索。“临时"配置不应该出现在生产环境,如果一定需要测试,请在独立的 dev 集群中做。对 Redis Cluster 来说,一致性不是可选项,是存活前提。