首页/数据/数据库速查/分布式锁续期与看门狗(watchdog)
Redis 运维与场景124 条

分布式锁续期与看门狗(watchdog)

业务执行时间可能超过锁 TTL,需要后台看门狗定期续期(仍是自己的锁才延长):Redisson 默认每 1/3 TTL 续一次;续期失败要尽快中止业务以免双写。

数据库速查数据更新于 2026-10-13

锁续期与看门狗是什么

分布式锁必须设置过期时间,否则持锁进程崩溃会永久死锁。但业务执行时间可能超过这个长度,于是需要后台看门狗定期延长过期时间——前提是锁仍属于自己,否则不能续。续期本身也有边界:进程长时间停顿时续期会一起停,锁过期后就可能出现两个持有者。

基本信息

续期条件锁的值仍等于自己写入的令牌才允许延长
续期间隔常见做法是锁时长度的三分之一
实现方式用脚本把比对与延长合并为一个原子操作
失败处理续期失败或网络异常时应尽快中止业务并核对数据
代价额外心跳流量,锁数量大时会放大实例压力

典型使用场景

  • 长耗时任务:导出、对账等执行时间不可预估,需要动态续期
  • 定时任务防重:同一任务在多实例部署时只允许一个执行
  • 库存与订单串行化:把并发操作收敛到单实例处理
  • 跨服务资源互斥:多个服务争用同一外部资源时做互斥
  • 与关闭钩子配合:任务结束时主动释放锁,而不是等它过期

续期脚本

续期脚本
-- 仍持有才刷新过期时间(毫秒),返回 1 表示续期成功
if redis.call('GET', KEYS[1]) == ARGV[1] then
  return redis.call('PEXPIRE', KEYS[1], ARGV[2])
else
  return 0
end

调用与释放

调用与释放
# 加锁:令牌唯一,过期时间 30 秒
SET lock:order:42 6f1d2c4e NX PX 30000
# 续期:每 10 秒执行一次(锁时长度的三分之一)
EVAL "if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('PEXPIRE', KEYS[1], ARGV[2]) else return 0 end" 1 lock:order:42 6f1d2c4e 30000
# 释放:比对令牌后删除,避免删掉别人的锁
EVAL "if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) else return 0 end" 1 lock:order:42 6f1d2c4e

参数与节奏

要素取值建议说明
锁时长预估最大执行时间加余量过短会频繁续期,过长会延长故障影响
续期间隔锁时长的三分之一留出容错窗口,避免临界过期
令牌随机值且唯一用于续期与释放时的归属校验
等待获取设定超时并快速失败避免请求堆积在抢锁上
续期返回零说明锁已经不是自己的:此时应立即中止业务,而不是继续执行下去。
进程长时间停顿时续期线程同样停止:锁过期后出现双持有者窗口,关键业务要在数据层加唯一约束等兜底。

易错点

  • 只在任务开始时续期一次:长任务中途锁仍会过期,互斥失效
  • 续期成功就认为绝对安全:停顿期间下游可能已被他人写入,需要数据层兜底
  • 释放锁不校验令牌:可能误删他人的锁,造成互斥被破坏
  • 锁时长设得过长:进程崩溃后其他实例要等很久才能取得锁

数据来源与参考

  • Redis 官方文档: 分布式锁
  • Redisson 文档: 看门狗

相关工具

​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌