Redisson WatchDog 机制通过自动续期解决锁提前释放问题:满足无参lock()或tryLock()传-1时启用,每10秒用Lua脚本原子校验并续期,默认TTL30秒;禁用显式leaseTime;需关注batchSize和watchdogTimeout配置,避免混用租期锁、unlock缺失及跨实例锁冲突。

Redisson 的 WatchDog 机制专为解决“业务执行时间不可预估”导致的锁提前释放问题而设计。它不依赖你精准估算耗时,而是在锁即将过期前自动重置 TTL,确保持有者未完成业务时锁不失效,业务结束或客户端宕机后又能及时释放,兼顾可靠性与安全性。
WatchDog 启用的前提条件
只有满足以下任一条件,WatchDog 才会自动启动:
- 调用 无参 lock():例如
lock.lock(),此时使用默认 30 秒 TTL,WatchDog 每 10 秒续期一次 - 调用 tryLock() 并传入 -1 作为 leaseTime:例如
lock.tryLock(5, -1, TimeUnit.SECONDS),表示等待最多 5 秒,但获取成功后启用自动续期
只要显式指定大于 0 的 leaseTime(如 lock.lock(15, TimeUnit.SECONDS)),WatchDog 就会被禁用——这是硬性规则,源码中明确互斥。
WatchDog 的续期原理与关键保障
它不是简单地“每隔 10 秒发一条 EXPIRE 命令”,而是通过三层协同实现高可靠续期:
-
Lua 脚本原子校验:每次续期都执行一段 Redis 端 Lua 脚本,先检查当前锁的 value 是否仍匹配本线程的唯一标识(如 UUID+线程 ID),再执行
PEXPIRE。避免了“检查时持有、续期前被别人抢走”的竞态 -
Netty 时间轮驱动:基于
HashedWheelTimer实现 O(1) 级别定时调度,低延迟、高吞吐,不阻塞主线程 -
全局状态精确管理:每把锁对应一个
ExpirationEntry,存于线程安全的EXPIRATION_RENEWAL_MAP中,记录线程 ID、重入次数和定时任务句柄,确保 unlock 时能精准取消续期任务
高并发下需关注的配置与边界
WatchDog 在大规模锁场景下并非完全“零配置无忧”,以下两点直接影响续期稳定性:
-
续期批处理上限:参数
lockWatchdogBatchSize默认为 100,表示每次续期任务最多处理 100 把锁。若同时持有锁数量远超该值(如批量任务启上百个线程各持一把锁),部分锁可能错过本轮续期,存在提前过期风险。可适当调大此值,但需权衡定时任务执行耗时 -
锁默认过期时间:参数
lockWatchdogTimeout默认 30000ms(30 秒),决定续期目标 TTL 和触发周期(约 10 秒)。若业务普遍耗时在 60 秒以上,建议将该值设为 60000,并确认网络与 Redis 响应稳定,避免因单次续期失败累积导致锁失效
典型误用与规避方式
实践中常见几类导致 WatchDog 失效的操作,需特别注意:
-
混用带租期 lock 与长任务:比如秒杀下单逻辑实际耗时 25 秒,却写成
lock.lock(20, TimeUnit.SECONDS),锁在第 20 秒强制释放,其他线程立即介入——应改用无参 lock - unlock 调用缺失或异常绕过:finally 块中未保证 unlock 执行,或 unlock 过程抛出未捕获异常,会导致 WatchDog 任务无法清理,虽不影响锁最终过期,但浪费客户端资源;建议配合 try-with-resources 或显式 finally + 异常日志
-
跨进程/多实例共享同一锁名但未隔离上下文:WatchDog 是客户端本地机制,不同 JVM 实例间不共享续期状态。若多个服务共用一个锁 key 却未区分业务上下文(如都用
"order_lock"),可能导致 A 实例续期时覆盖 B 实例的 value,引发误删锁。应按业务维度细化锁粒度,如"order_lock:1001"











