连载中 10/20

ZooKeeper 分布式锁:临时顺序节点的排队艺术

2026-06-29 · 2930 阅读 · 0 评论 · 0 赞

用第一篇的三个指标验收

前三篇攒足了 Redis 锁的修补经验,现在拿第一篇立下的三个硬指标(互斥、防死锁、可辨识)去验收 ZooKeeper 方案。先看流程——抢锁 lock:card:8801,四步:

1. 在 /locks/card-8801/ 下创建「临时顺序节点」
   → 得到 /locks/card-8801/seq-0000000042(序号全局单调,并发不重号)
2. 列出父节点下全部子节点,按序号排序
3. 自己是最小 → 直接持锁,进临界区
4. 不是最小 → 只 Watch 序号比自己小一位的前驱节点,然后等待
   前驱删除时收到通知 → 回到第 2 步再看一遍排位

释放锁更简单:删掉自己的那个临时节点。四步走完,三个指标逐一过验。

互斥:最小序号只有一个

「排位第一」是 ZooKeeper 集群仲裁过的事实:顺序节点的序号由服务端分配(ZAB 过半提交),并发创建绝不重号;同一时刻父目录下序号最小的节点有且只有一个。持锁权 = 最小序号,这是集群级共识背书的互斥,不存在 Redis 那种「主从各说各话」的窗口。附带一条免检福利:公平。序号即到达顺序,先到先得,天然无饿死——Redis 篇里要额外付吞吐代价才买到的 FairLock,这里是出厂标配。

防死锁:会话蒸发,锁跟着蒸发

这是 ZK 方案对 Redis 方案最体面的一击。临时节点绑定会话(第 8 篇),持锁客户端崩溃 → 连接断 → 心跳停 → 会话过期 → 锁节点被服务端自动删除 → 下一位立刻接棒。全流程没有 TTL 参数,没有看门狗线程,没有「过期时间设多长」的赌博——持有人活着,锁就活着;持有人死了,锁十分钟内(sessionTimeout)自动善后。锁的生命周期与进程活性天然绑定,防死锁是协议级保证,不是参数级补救

可辨识与防羊群:只盯前一个

删锁删的是「自己的节点」,别人的删不了也轮不到删——可辨识在节点归属上天然成立。流程里最精巧的一笔是第 4 步的 「只 Watch 前驱,不 Watch 全体」。反面教材:一百个等待者全都 Watch 锁目录本身,持锁者一释放,一百个通知同时炸响,一百个客户端同时扑回来抢位——这就是第 6 篇的惊群(羊群效应)在 ZK 上的重演。正面做法:每个人只盯自己前面的那一个,锁释放一次,恰好唤醒一个等待者,队伍像传送带一样一格一格向前走,事件量从 O(N) 降到 O(1)。

指标Redis 锁的实现代价ZK 锁的获得方式
互斥SET NX + Lua,但主从切换有窗口最小序号,集群共识背书
防死锁TTL + 看门狗线程,参数赌命临时节点绑会话,协议级保证
可辨识UUID value + Lua 校验删除节点归属天然成立
公平FairLock 额外 list 排队,吞吐受损序号排队,出厂标配
防惊群pub/sub 广播,唤醒全体只 Watch 前驱,一次唤醒一人

生产别手写:用 Curator

流程看着清爽,手写全是暗礁:会话抖动后 Watch 没了要重挂、连接中断要重试、节点删失败要兜底。生产直接用 Curator(Netflix 出品的 ZK 客户端),可重入互斥锁十行搞定:

InterProcessMutex lock =
    new InterProcessMutex(curatorClient, "/locks/card-8801");
if (lock.acquire(5, TimeUnit.SECONDS)) {     // 最多等 5 秒
    try {
        // 临界区业务(可重入:同线程反复 acquire 计数+1)
    } finally {
        lock.release();                       // 计数-1,减到 0 才真删节点
    }
}

它还把重入做成了节点内计数(同 Redisson 的 Hash 思路),把公平锁、读写锁、信号量、屏障一并提供。代价也要老实交代:ZK 锁的性能天花板远低于 Redis——每次写锁节点都要走一遍过半提交,QPS 量级是「千」不是「十万」,所以它适合锁粒度粗、竞争频次低的场景(选主、任务分配、配置仲裁),不适合秒杀扣减这种高频抢锁。下一篇盘点 ZK 锁自己的坑:会话误判、幽灵复活、性能天花板——裁判也有看走眼的时候。

503

10 年全栈工程师 · 503咖啡馆主理人

#ZooKeeper#分布式锁#临时顺序节点#羊群效应#Curator

评论 (0)

相关推荐

连载中 12/20

排查四件套:jstack、jmap、jstat、jcmd 的实战分工

jstack 看线程在干什么,jmap 看堆里装了什么,jstat 看运行时在变什么,jcmd 是统一入口。四把刀各管一段,配合着用没有查不动的现场。

#jstack#jmap#jstat#jcmd#排查工具
2026-09-16 · 4 阅读 · 0 评论 · 0 赞
连载中 11/20

GC 日志:把回收过程翻译成人话

一行 GC 日志里塞着七种信息:谁触发的、收了哪、停了多久、活了哪些。加上 -Xlog 配置,再加上日志分析工具,GC 不再是只能盯监控曲线的黑盒。

#GC日志#Xlog#日志分析#GC监控#Full GC排查
2026-09-16 · 13 阅读 · 0 评论 · 0 赞
连载中 10/20

ZGC:亚毫秒停顿是怎么炼成的

百 G 大堆停顿不到一毫秒,靠的是把搬家全部挪到并发阶段——着色指针让引用自带状态,读屏障让搬运中的对象依然可访问。代价是吞吐与内存,收益是停顿与堆大小解耦。

#ZGC#着色指针#读屏障#亚毫秒停顿#分代ZGC
2026-09-15 · 8 阅读 · 0 评论 · 0 赞