连载中 10/20

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

2026-06-29 · 2933 阅读 · 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)

热门推荐

连载中 11/22

主从搭建实操:从零配出一主两从

光讲原理不过瘾?手把手搭一主两从:my.cnf 六个参数、复制账号、GTID、CHANGE REPLICATION SOURCE TO、SHOW REPLICA STATUS 验收,附翻车排查清单。

#MySQL#主从复制#GTID#主从搭建#高可用
2026-05-07 · 10101 阅读 · 0 评论 · 0 赞
连载中 16/22

连接池:HikariCP 参数与连接风暴

连接池不是越大越好:8 核机器配 1000 连接反而更慢的数学原理,HikariCP 四个必调参数,maxLifetime 与 wait_timeout 的隐形陷阱。

#MySQL#连接池#HikariCP#maxLifetime#连接风暴
2026-05-10 · 9873 阅读 · 0 评论 · 0 赞
连载中 4/16

缓存穿透:恶意 ID 打穿 MySQL 的四道防线

请求的数据在缓存和数据库里都不存在时,缓存形同虚设。聊聊参数校验、空值缓存、布隆过滤器、限流熔断四道防线的原理与组合打法。

#Redis#缓存穿透#布隆过滤器#高可用
2026-05-16 · 9294 阅读 · 21 评论 · 287 赞