什么是脑裂?分布式系统如何避免脑裂?
简化版
脑裂是指集群因为网络分区、心跳误判或故障切换异常,出现两个或多个节点都以为自己是主节点,并同时对外提供写服务。它会造成数据分叉、重复调度、双主写入等严重问题。常见防护是多数派选主、租约、Fencing Token、STONITH/强制隔离、共享存储写保护、客户端只信任最新任期。
详细版
脑裂常见于主备、高可用、分布式锁、调度器、存储集群等场景。根因通常是:节点之间通信异常,但节点本身没有宕机,于是旧主和新主同时存在。
常见解决手段:
| 手段 | 作用 |
|---|---|
| 多数派 Quorum | 只有拿到多数派的分区才能选主或写入 |
| 任期/epoch | 新主任期更大,旧主请求被拒绝 |
| Fencing Token | 每次获得主身份拿到递增 token,下游只接受最大 token |
| 租约 Lease | 主身份有有效期,过期必须续约成功才能继续写 |
| STONITH/隔离 | 确认旧主不可写后再让新主接管 |
| 共享存储保护 | 存储层拒绝旧主写入 |
脑裂最怕“两个主都能写”。只靠心跳不足以避免脑裂,因为心跳断了不代表对方真的死了,可能只是网络隔离。
完整版教学
一、用主备切换理解脑裂
假设有一个主库 A 和备库 B。正常情况下 A 写数据,B 同步。现在 A 和 B 之间网络断了,B 收不到 A 的心跳,于是认为 A 挂了,自己提升为主。如果此时 A 其实还活着,并且客户端仍然能访问 A,那么 A 和 B 都在以主库身份写入,这就是脑裂。
脑裂的危险不是“有两个节点”,而是“两个节点都认为自己有权做不可重复的事情”。两个主库写同一份数据会产生冲突;两个调度器同时调度任务会重复执行;两个锁持有者同时操作资源会破坏互斥。
二、心跳为什么不能证明对方死亡
分布式系统里,收不到心跳只能说明“我现在联系不上对方”,不能说明“对方已经停止工作”。可能是网络丢包、交换机故障、安全组规则变化、GC 暂停、CPU 打满,也可能是自己这边网络有问题。
如果系统把“心跳超时”等同于“旧主死亡”,就很容易脑裂。正确设计要承认不确定性:联系不上旧主时,不能仅凭单点判断就让新主随便写,必须引入多数派、租约、隔离或下游 fencing。
三、多数派为什么能防止双主
如果集群有 3 个节点,任何主节点必须拿到至少 2 个节点认可。网络分区后,不可能出现两个分区都拿到多数派,因为两个多数派集合必然相交。这样少数派即使还活着,也不能继续作为主处理写请求。
Raft、ZooKeeper、etcd 都依赖类似思想。Leader 必须属于多数派,日志也要复制到多数派才能提交。这样即使少数派旧 Leader 还以为自己是 Leader,它也无法提交新的写入。
四、Fencing Token 是防脑裂的最后一道闸
多数派能减少脑裂,但在外部资源场景里,还需要 fencing。比如分布式锁保护的是数据库或文件系统,旧锁持有者可能因为长时间 GC 暂停而错过续约,锁被新客户端拿到。旧客户端恢复后,如果继续写外部资源,就破坏互斥。
Fencing Token 的做法是:每次成功获得锁或主身份时,发一个单调递增 token。外部资源只接受 token 更大的写请求,拒绝旧 token。这样即使旧主“醒过来”,也会被资源层挡住。这个思路在锁、调度、存储写入里非常重要。
五、租约和时钟的坑
租约看起来简单:主节点在租约有效期内才有权写。但租约依赖时间,如果机器时钟漂移或 GC 暂停,旧主可能误以为租约还有效。更稳的做法是租约续约要经过多数派确认,下游仍然校验 epoch/token。不要只靠本地时间判断自己是不是主。
六、工程上如何处理脑裂风险
第一,选主必须基于多数派,避免少数派独立称主。第二,主身份要有任期号,所有写请求带任期,下游拒绝旧任期。第三,关键外部资源要支持 fencing token。第四,故障切换前尽量隔离旧主,比如断电、停进程、取消挂载。第五,客户端要从权威注册中心或配置中心获取主地址,不能长期缓存旧主。
七、常见误区与追问
这道题不能只背概念,要把「脑裂问题」放回真实分布式系统里解释:谁发起、谁协调、状态如何变化、失败后怎么兜底,以及它和性能、可用性、一致性的取舍。
| 回答层次 | 要讲清的内容 | 容易漏掉的边界 |
|---|---|---|
| 核心结论 | 脑裂是集群网络分区后多个节点都认为自己是主,导致双写和数据分叉 | 不要停在名词解释 |
| 流程机制 | 主节点与备节点网络中断 -> 备节点误判主故障并升主 -> 原主仍对外服务 -> 出现双主写入 -> 通过仲裁和 fencing 阻止旧主写 | 说明触发方、参与方、状态变化和兜底 |
| 工程取舍 | 主备集群网络隔离时,如果两个节点都对外写入,恢复后同一订单可能出现两个不同状态 | 分布式基础题不能只背概念,必须落到网络不可靠、节点会故障、数据有副本这些前提 |
脑裂问题 面试拆解:
1. 主节点与备节点网络中断
2. 备节点误判主故障并升主
3. 原主仍对外服务
4. 出现双主写入
5. 通过仲裁和 fencing 阻止旧主写
记忆钩子:先定义问题,再说明一致性、可用性、分区、复制、时钟和故障模型的取舍;回答时要紧扣「脑裂问题」这道题,不要把相邻概念混成一段泛泛的分布式套话。
- 误区:脑裂只是主从切换慢。 脑裂的核心是同时存在多个主并写入,数据会分叉。
- 误区:心跳检测就能彻底避免脑裂。 心跳只能发现不可达,无法区分对方宕机还是网络分区。
- 误区:恢复网络后自动合并就行。 双写数据可能冲突,必须有仲裁、隔离和人工修复策略。
- 追问:如何防脑裂? 多数派仲裁、租约、fencing token、STONITH、只允许仲裁成功一侧写。
- 追问:fencing 是什么? 给新主一个递增令牌,下游只接受最新令牌写入,旧主即使存活也写不进去。
- 追问:为什么奇数节点常见? 多数派投票下奇数节点资源利用更高,3 节点可容忍 1 节点故障。
八、加强记忆
脑裂就是网络分区或误判导致多个节点都以为自己是主,并同时写同一资源。心跳超时不能证明对方死亡,所以不能只靠心跳切主。防脑裂的核心是:多数派保证只有一个分区有资格当主,任期/epoch 区分新旧主,Fencing Token 让外部资源拒绝旧主写入,必要时强制隔离旧主。关键点:选出新主不够,还要让旧主写不进去。