什么是 Redis 集群脑裂问题,怎么解决?

#中间件#面试八股

什么是 Redis 集群脑裂问题,怎么解决?

结论:脑裂指网络分区导致集群同时出现两个"主":原主节点与多数派失联但仍在接受客户端写入,哨兵/Cluster 在另一侧选出新主。分区恢复后原主降级为从并执行全量同步,清空自己那份"未被复制"的写入,这部分数据就永久丢失了。根因不是 Redis 有 bug,而是异步复制 + 自动故障转移的固有代价。

一、脑裂的完整时间线

初始:主 M(有从 S1、S2),哨兵 3 个

T1  网络分区:M 与哨兵、S1、S2 断开,但 M 与部分客户端仍连通
T2  哨兵判定 M 客观下线(quorum 达成),选 S1 为新主 M'
T3  客户端(另一侧)继续向 M 写入 W1(M 已不在集群里,但没人告诉它)
    ── 此时集群有两个主:M(旧)与 M'(新)──   ← 脑裂
T4  网络恢复:M 重新加入,收到 M' 的同步指令,降级为从
T5  M 执行全量同步:清空本地数据,加载 M' 的 RDB
    ⇒ W1 永久丢失(从未复制到任何从节点)

关键点:丢失的窗口 = T2 到 T4 之间旧主接受的写入。分区持续越久、旧主写入越多,丢失越多。

二、怎么减少损失

手段作用局限
min-replicas-to-write至少 N 个健康从节点才允许写分区时旧主会被拒绝写,直接减少脑裂写入
min-replicas-max-lag从节点延迟不超过 N 秒才算健康与上一条配合,避免"延迟很久的从"仍算健康
客户端侧校验写前确认连的是当前主需改客户端或走代理
业务幂等 + 对账丢数据后可修复只能兜底,不能防丢
缩短 cluster-node-timeout更快判定故障、更快切换太短会因网络抖动误切换
# 至少 1 个从节点、且延迟 ≤ 10 秒才允许写入
min-replicas-to-write 1
min-replicas-max-lag 10
效果分析:
  分区后旧主 M 发现健康从节点数 = 0 < 1
  → M 拒绝所有写命令,返回 NOREPLICAS 错误
  → 客户端快速失败,业务可重试到新主
  → 脑裂窗口内不再产生"注定丢失"的写入

注意这仍是降低概率与影响,不是彻底解决:如果分区前旧主已有少量写入还没同步,那部分仍会丢。Redis 官方文档也明确说明该配置只能"尽力减少"数据丢失。

三、为什么不能彻底解决

RPO = 0 需要同步复制(写主库必须等从库确认)
  ├─ 同步复制 → 从库慢就拖慢主库 → 可用性与延迟受损
  └─ Redis 选择异步复制 → 主库写成功即返回 → 必然存在未复制窗口

因此脑裂的取舍是 CAP 中的选择:
  分区发生时,Redis 选 A(可用性),牺牲 C(一致性)

想要强一致,需要换系统:etcd/ZooKeeper(Raft/ZAB,多数派写入才算成功)或数据库的同步复制;缓存层则应假设"数据可能丢",用 DB 兜底 + 幂等重放。

三、架构侧的三层兜底

第 1 层:配置兜底
  min-replicas-to-write > 0  → 分区侧旧主直接拒写
  cluster-node-timeout 合理   → 不因抖动误切换

第 2 层:业务兜底
  写路径以 DB 为准,Redis 只做缓存/计数
  消息队列可靠投递,消费端幂等

第 3 层:数据兜底
  定时对账(DB vs 缓存 / 上游 vs 下游)
  差异自动修复,并记录丢失窗口用于复盘

判断是否"真的需要防脑裂"只看一个指标:分区期间旧主接受的写入,丢了会不会造成资损或不可逆状态。如果是缓存数据,丢了重新加载即可;如果是扣款、库存这类状态,就必须让 DB 或可靠 MQ 承担最终正确性。

四、常见追问

  • 问:脑裂一定丢数据吗?答:不一定。若旧主在分区期间没有任何写入,或写入恰好已同步给从节点,就不会丢;丢的是"未复制的写入"。
  • 问:哨兵模式下 min-replicas-to-write 有用吗?答:有用,它作用在主节点自身,与用哨兵还是 Cluster 无关。
  • 问:为什么分区恢复后旧主要做全量同步而不是增量?答:旧主已降级为从,且其数据集与主节点分叉(有主节点没有的写入),无法用 offset 续传,只能全量重建。
  • 问:能不能让旧主拒绝降级、保留自己的数据?答:那样会形成真正的双主,冲突无法自动合并;Redis 选择"以新主为准",牺牲少数派写入。
  • 问:Cluster 下脑裂表现有什么不同?答:没有哨兵,靠 gossip + 多数派投票;旧主若失去多数派连接,会进入 cluster_state:fail 并停止服务,同样无法完全避免未复制写入丢失。
  • 问:业务上怎么兜底?答:关键写操作落 DB 后再更新缓存、用消息队列做可靠投递、定期对账修复;把 Redis 当"可能丢的数据源"来设计。

小结

  • 脑裂 = 网络分区 + 自动切换 + 异步复制,三者叠加的必然结果。
  • min-replicas-to-write / min-replicas-max-lag 是唯一的内建缓解手段,能显著缩小丢失窗口。
  • 真正的解法在架构层:接受缓存可丢,用数据库与对账保证最终正确。
笔记加载中…