← 返回题目列表

如何用 ZooKeeper 实现分布式锁?为什么用临时顺序节点?

高频 中等 第 5 / 26 题 更新于 2026/07/28
ZooKeeper分布式锁临时顺序节点

简化版

ZooKeeper 实现分布式锁靠临时顺序节点(EPHEMERAL_SEQUENTIAL):所有客户端在同一个父节点下创建临时顺序子节点,ZK 会给它们编号(递增)。序号最小的那个客户端获得锁;其他客户端不去反复轮询,而是只 watch(监听)自己前一个节点,前一个被删除(锁释放)时才被唤醒去尝试。用临时节点是因为客户端断连/宕机时 ZK 会自动删除它的节点(防死锁),用顺序节点是为了实现公平锁 + 只监听前驱避免惊群

详细版

加锁流程

  1. 客户端在锁的父节点(如 /lock)下创建一个临时顺序节点,如 /lock/req-0000000001
  2. 获取父节点下所有子节点,排序,判断自己的序号是不是最小的。
  3. 是最小 → 获得锁,执行业务。
  4. 不是最小 → 找到比自己小一位的那个节点(前驱),对它注册 watch 监听,然后阻塞等待。
  5. 当前驱节点被删除(前一个客户端释放锁/宕机),watch 触发,客户端被唤醒,回到第 2 步重新判断(此时自己通常成了最小)。

解锁:删除自己创建的节点。临时节点在会话结束(正常解锁或宕机断连)时也会被 ZK 自动删除。

两个关键选型

  • 临时(Ephemeral):会话断开自动删除 → 持锁客户端宕机时锁自动释放,天然防死锁,不需要设过期时间。
  • 顺序(Sequential):ZK 保证编号单调递增 → 天然形成排队顺序(公平锁),且每个节点只监听前一个,避免所有人监听同一节点造成的「惊群效应」。

完整版教学

一、为什么临时节点能防死锁

Redis 锁靠「过期时间」防死锁,但过期时间不好设(短了误删、长了等太久)。ZooKeeper 用临时节点优雅解决:临时节点的生命周期绑定客户端的会话(Session),客户端和 ZK 之间靠心跳维持会话。一旦客户端宕机/网络断开,心跳超时,会话失效,ZK 自动删除该客户端创建的所有临时节点——锁随之释放。不需要预估过期时间,也不会因为「业务超时」误删(会话没断锁就一直在),比 Redis 的过期机制更自然。

二、为什么用顺序节点 + 只监听前驱

如果所有客户端都创建普通节点、都去监听「锁节点」是否释放,那么锁一释放,所有等待者同时被唤醒去抢,只有一个成功,其余又继续等——这就是「惊群效应(herd effect)」,大量无效唤醒和竞争,浪费资源。

ZK 用顺序节点把等待者排成一队(按编号),每个客户端只监听紧挨在自己前面的那一个节点。这样锁释放时(最小节点删除),只有排在它后面的下一个被唤醒,其余人纹丝不动。既实现了公平(按创建顺序 FIFO 获得锁),又消除了惊群(每次只唤醒一个)。这是 ZK 分布式锁设计的精髓。

记忆点:临时节点 = 防死锁(宕机自动释放);顺序节点 + 只 watch 前驱 = 公平锁 + 防惊群。两个特性结合,才是 ZK 锁优于 Redis 锁的地方。

三、ZK 锁为什么可靠(CP 特性)

ZooKeeper 是 CP 系统,基于 ZAB 协议,写操作要过半节点确认才成功。所以「创建锁节点」这个写操作一旦返回成功,就意味着已经被多数派持久化,不会像 Redis 主从那样因异步复制 + 主宕机而丢锁。这让 ZK 锁在一致性上强于 Redis 锁,适合对正确性要求高的场景。代价是:每次加解锁要走共识(过半确认),性能比 Redis 低(Redis 是内存单点操作,ZK 要多节点协调)。

四、ZK 锁的不足

  • 性能较低:加解锁涉及节点创建/删除 + watch,且要过半确认,QPS 不如 Redis。
  • 羊群问题需正确实现:如果实现时偷懒监听父节点而非前驱,仍会惊群。
  • 会话超时的误判:如果客户端因长时间 GC 或网络抖动导致心跳超时,ZK 会认为它挂了、删除临时节点、释放锁,但客户端其实还活着——和 Redlock 的 GC 问题类似,ZK 也不能完全避免(但发生概率和影响可控)。
  • 依赖 ZK 集群:要额外维护 ZK 集群。

五、Redis 锁 vs ZooKeeper 锁(速记)

维度Redis 锁ZooKeeper 锁
CAPAP(性能优先)CP(一致性优先)
防死锁过期时间(要续期)临时节点(会话断自动删)
公平性默认非公平顺序节点天然公平
性能高(内存单点)较低(共识+watch)
可靠性主从可能丢锁过半确认不易丢锁
适用高并发、容忍极小概率丢锁强一致、正确性优先

六、常见误区与追问

这道题不能只背概念,要把「ZooKeeper 分布式锁」放回真实分布式系统里解释:谁发起、谁协调、状态如何变化、失败后怎么兜底,以及它和性能、可用性、一致性的取舍。

回答层次要讲清的内容容易漏掉的边界
核心结论ZooKeeper 锁常用临时顺序节点,序号最小者获得锁,前驱节点删除后下一个节点被唤醒不要停在名词解释
流程机制创建临时顺序节点 -> 获取同目录节点排序 -> 序号最小获得锁 -> 否则监听前一个节点 -> 前驱删除后重新判断 -> 会话断开临时节点自动删除说明触发方、参与方、状态变化和兜底
工程取舍客户端创建 /lock/seq-00000003,若前面有 seq-00000002,就只 watch 这个前驱节点分布式锁只能控制临界区进入,不能替代业务幂等和状态校验
ZooKeeper 分布式锁 面试拆解:
1. 创建临时顺序节点
2. 获取同目录节点排序
3. 序号最小获得锁
4. 否则监听前一个节点
5. 前驱删除后重新判断
6. 会话断开临时节点自动删除

记忆钩子:先说为什么单机锁失效,再说明加锁、过期、续期、解锁、防误删和故障恢复;回答时要紧扣「ZooKeeper 分布式锁」这道题,不要把相邻概念混成一段泛泛的分布式套话。

  • 误区:所有等待者都监听同一个节点。 应监听前驱节点,避免锁释放时惊群。
  • 误区:ZooKeeper 锁不需要释放。 正常完成要删除节点,异常断开才靠临时节点自动释放。
  • 误区:ZooKeeper 锁性能一定比 Redis 高。 ZooKeeper 更强一致,但写入和 watch 成本通常高于 Redis。
  • 追问:为什么用临时节点? 客户端会话失效时节点自动删除,避免死锁。
  • 追问:为什么用顺序节点? 天然形成排队顺序,避免所有客户端抢同一把锁。
  • 追问:ZooKeeper 锁适合什么? 强一致协调、选主、低频关键互斥场景。

七、加强记忆

ZooKeeper 分布式锁:所有客户端在父节点下建临时顺序节点,序号最小者持锁,其余只监听前一个节点、前驱删除时才被唤醒。临时节点让客户端宕机时会话失效、节点自动删除,天然防死锁(不用设过期);顺序节点 + 只 watch 前驱实现公平锁并消除惊群。ZK 是 CP 系统,写过半确认,锁不易丢失,可靠性强于 Redis 主从锁,但性能较低。高并发用 Redis,强正确性用 ZK。