连载中 3/20

锁误删与续期:看门狗从哪来

2026-06-25 · 6075 阅读 · 0 评论 · 0 赞

两头堵的困局

上一篇结尾留了个死结:锁的过期时间(比如 10 秒)必须覆盖业务执行时长,但业务时长没有上限——数据库抖 30 秒、GC 停 20 秒,10 秒的锁就是纸糊的。直觉的解法是把过期时间往大了设:设 5 分钟,业务再慢也跑得完。但反过来想:持有人真的崩了呢?所有请求干等 5 分钟才敢抢锁,一家门店宕机,全公司的储值卡业务瘫痪 5 分钟——防死锁的代价变成了慢性死亡。过期时间设短是「锁跑赢业务」,设长是「业务拖死锁」,两头堵。破局的思路要掉个头:锁的生死别靠猜,靠持有人「还活着」这个事实来保证——活着就续期,死了没人续,锁自然过期。

看门狗:一个线程的忠诚契约

这就是看门狗(watchdog)机制的全部思想。抢到锁之后,启动一个后台线程,每隔一段时间(通常是 TTL 的三分之一)检查一次:业务还在跑吗?在跑就给锁续期,把 TTL 重置回初始值:

时间线(TTL = 10s,看门狗周期 = 10/3 ≈ 3.3s):
t=0s    抢到锁,TTL=10s,看门狗启动
t=3.3s  业务还在跑 → 续期,TTL 重置为 10s
t=6.6s  业务还在跑 → 续期,TTL=10s
t=9.9s  业务还在跑 → 续期,TTL=10s
……业务跑多久,锁就活多久……
t=Xs    业务完成 → 主动释放锁,看门狗停止
  或
t=Ys    进程崩溃 → 没人续期 → 最迟 10s 后锁自动过期

这个设计最妙的地方在于崩溃安全是白送的:进程死了,看门狗线程跟着死了,没人续期,锁按 TTL 自动释放——「防死锁」和「业务不受时长限制」两个目标同时达成,过期时间再也不用赌业务时长,设个 10 秒、30 秒都行。契约的本质是:锁的存续绑定在「持有进程还活着」这个可自动失效的事实上,而不是绑定在「业务什么时候做完」这个不可预测的变量上。

续期本身也要原子

续期不是无脑 EXPIRE。续期那一刻,锁可能刚好过期、别人可能刚好抢到——上一篇的教训在续期这里原样复现:校验身份和重置 TTL 必须原子,还是 Lua:

-- 续期脚本:锁还在且是我的,才重置 TTL
if redis.call("GET", KEYS[1]) == ARGV[1] then
    return redis.call("PEXPIRE", KEYS[1], ARGV[2])
else
    return 0    -- 锁没了或易主了,续期失败
end

续期失败要立刻警觉——这意味着你已经不是持有人了,临界区里的业务要尽快中断或走失败处理,而不是继续蒙头跑完。一个诚实的实现:续期失败时设置一个「锁丢失」标志,业务代码在关键写操作前检查标志,发现丢锁立即回滚自己已做的动作。能不能做到,决定了这套锁是玩具还是工程。

看门狗也看不见的角落

看门狗不是免死金牌,两个角落要交底。其一,STW 停顿把看门狗一起冻住:Full GC 停 30 秒,续期线程也停 30 秒,锁过期、B 抢走、B 干活、GC 结束后 A 复活——A 的续期脚本一跑,身份校验失败(锁已易主),返回 0,A 知道自己丢锁了,这算体面收场;怕的是 A 复活后不看返回值继续干活。其二,进程假死:线程还在、没崩溃,但网络断了一直续不上期,锁照样过期易主——看门狗续期依赖 Redis 连接,「活着」的定义是「还能跟 Redis 说话」,说不上话的活着,等于死了。这两类场景的根治靠下游兜底(fencing token,第 16 篇),锁只能把危险概率压小,压不到零。

场景无看门狗有看门狗
业务跑 60 秒,TTL 10 秒t=10s 锁过期,互斥破碎持续续期,锁活到业务结束
进程 t=2s 崩溃锁按 TTL 释放(本就该如此)续期线程同死,锁按 TTL 释放
Full GC 停 30 秒锁过期,GC 后误以为仍持有续期脚本校验失败,可感知丢锁

到这里,一把单机 Redis 锁的正确姿势已经齐了:SET NX PX 加随机 value 抢锁、Lua 校验身份释放、后台线程 Lua 续期。三件套自己写也就百来行,但边界情况(重入、公平排队、读写锁、集群漂移)每加一个,复杂度翻一倍——有轮子为什么不用。下一篇登场的就是 Redis 生态的锁库一哥 Redisson:看门狗它给你造好了,可重入它给你实现了,我们从它的一段 Hash 结构讲起。

503

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

#分布式锁#看门狗#续期#GC停顿#Lua脚本

评论 (0)

相关推荐

连载中 12/20

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

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

#jstack#jmap#jstat#jcmd#排查工具
2026-09-16 · 5 阅读 · 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 赞