什么是 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是唯一的内建缓解手段,能显著缩小丢失窗口。- 真正的解法在架构层:接受缓存可丢,用数据库与对账保证最终正确。