Zookeeper-假死闹裂
该问题就是服务集群因为网络震荡导致的多主多从问题,解决方案就是设置服务切换的超时时间,但也同时会导致无法达到高可用的要求。
一句话版本到此为止。但”假死”与”脑裂”其实是两个相关却不同的故障形态,面试和排障中都值得拆开讲清楚:它们各自的成因、表现、以及 ZooKeeper 如何用会话与配额机制把它们约束在可控范围内。
假死:活着但”被认为死了”
假死(zombie/半死状态)指节点进程还活着、甚至还在对外服务,但由于 GC 长停顿、磁盘 IO 阻塞、网络分区等原因,无法在会话超时时间内与集群通信,于是被集群判定死亡:Leader 被重新选举、临时节点被清理、锁被释放。
危险在于:假死节点恢复后仍可能认为自己持有锁或仍是 Leader,继续写数据——这就是假死引发的”双主”写冲突。ZooKeeper 的约束手段:
- 会话(session)+ 临时节点(ephemeral node):会话过期,临时节点立即删除,锁/注册信息随之失效
- Fencing Token(隔离令牌):ZooKeeper 的 zxid/epoch 单调递增,假死 Leader 恢复后携带旧 epoch 写请求会被 follower 拒绝——存储层不认旧主,双主写不进数据
- 超时参数权衡:
tickTime、sessionTimeout设得小,故障切换快但易误杀(一次长 GC 就触发切换);设得大,误杀少但故障恢复慢。这就是”切换超时时间”与”高可用”之间的天然矛盾
脑裂:一个集群变成两个”自称合法”的集群
脑裂(split-brain)指网络分区把集群切成两半,两边各自选举 Leader、各自接受写请求,出现数据分叉。
ZooKeeper 靠 ZAB 协议的多数派(quorum)原则 从根本上限制脑裂的破坏力:
- 选举需要超过半数节点投票,写提交需要超过半数节点 ack
- 网络分区后,只有包含多数派的那一半能选出 Leader 并继续服务;少数派那一半进入只读或不可服务状态
- 分区恢复后,少数派以多数派数据为准回滚对齐(按 zxid 截断未提交事务)
所以严格说,ZooKeeper 不会”双写脑裂”,只会”一边可用一边不可用”——它用可用性换一致性(CP 系统)。这也是为什么 ZK 集群必须部署奇数节点:2n+1 节点能容忍 n 台故障,而 2n 节点同样只能容忍 n-1 台,白白多一台机器。
假死与脑裂的关系
| 维度 | 假死 | 脑裂 |
|---|---|---|
| 触发 | 单节点停顿/分区 | 集群网络分区 |
| 表现 | 旧主”复活”后越权写 | 两个分区各自有主 |
| ZK 对策 | 会话过期 + epoch fencing | quorum 多数派 |
| 代价 | 超时参数两难 | 少数派不可用(CP) |
两者共同的根因是分布式系统中”死亡”无法被准确判定:只能靠超时猜测,而猜测就会出错。所有对策(会话、epoch、quorum)的本质都是”即使判定错了,也不让错误的数据写进去”。
对使用者的实践建议
- 锁/选主场景务必使用临时节点 + 会话,不要自己用永久节点模拟(进程崩溃后锁永不释放)
- 客户端要监听连接状态事件(
Disconnected/Expired),会话过期后重建状态而非续用旧句柄 sessionTimeout建议设为 tickTime 的 5-10 倍并结合业务 GC 情况压测,避免”GC 一抖就换主”- 理解 CP 取舍:需要”分区时仍可读”的场景(如服务发现缓存读)应在客户端加本地缓存兜底,而不是指望 ZK 分区可用
“假死脑裂”四个字背后,是分布式系统对故障判定与数据一致性的全部妥协艺术:判不死,就防错写;防不住双主,就让旧主写不进。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 非鱼小站!
评论
WalineDisqus





