Redisson 看门狗(Watch Dog)机制实现原理
Redisson 的 Watch Dog 机制是分布式锁的核心组件之一,用于 自动续期 锁的过期时间,防止业务逻辑执行时间超过锁的持有时间,导致锁提前释放而引发并发问题。以下是其实现原理的详细分析:
1. 看门狗的核心作用
问题背景:
当线程获取锁后,如果业务执行时间超过锁的过期时间(如设置的leaseTime=30s
),锁可能会被其他线程获取,导致数据不一致。解决方案:
Watch Dog 会 定期检查 并 自动延长锁的过期时间,确保业务执行完成前锁不会失效。
2. 看门狗的工作流程
(1)加锁时启动看门狗
当使用 tryLock()
或 lock()
方法获取锁时:
如果 不指定
leaseTime
(即锁无固定过期时间),Redisson 会 自动启动 Watch Dog。如果 指定
leaseTime
(如lock.lock(10, TimeUnit.SECONDS)
),则 不会启动 Watch Dog(由业务自行控制锁的释放)。
关键代码(RedissonLock.class)
java
复制
下载
// 如果未指定 leaseTime,则启动 Watch Dog if (leaseTime == -1) { // 设置默认过期时间(30秒) internalLockLeaseTime = lockWatchdogTimeout; // 启动 Watch Dog scheduleExpirationRenewal(threadId); }
(2)Watch Dog 的续期逻辑
Watch Dog 本质上是一个 后台定时任务,默认每 10 秒 检查一次锁的状态,并重置锁的过期时间(续期至 30 秒)。
续期任务(renewExpiration()
)
java
复制
下载
protected void renewExpiration() { // 1. 获取当前线程的锁信息 ExpirationEntry entry = EXPIRATION_RENEWAL_MAP.get(getEntryName()); if (entry == null) { return; } // 2. 创建定时任务,每 10 秒执行一次 Timeout task = commandExecutor.getConnectionManager() .newTimeout(new TimerTask() { @Override public void run(Timeout timeout) { // 3. 检查锁是否仍被当前线程持有 if (EXPIRATION_RENEWAL_MAP.containsKey(getEntryName())) { // 4. 执行 Lua 脚本,延长锁的过期时间 RFuture<Boolean> future = renewExpirationAsync(threadId); future.onComplete((res, e) -> { if (e != null) { log.error("Can't update lock expiration", e); return; } if (res) { // 5. 递归调用,继续续期 renewExpiration(); } }); } } }, lockWatchdogTimeout / 3, TimeUnit.MILLISECONDS); // 默认 10 秒(30s / 3) entry.setTimeout(task); }
Lua 续期脚本
lua
复制
下载
-- KEYS[1]: 锁的 Key -- ARGV[1]: 过期时间(30s) -- ARGV[2]: 客户端唯一标识(UUID + 线程ID) -- 1. 检查锁是否仍由当前线程持有 if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then -- 2. 延长锁的过期时间 redis.call('pexpire', KEYS[1], ARGV[1]); return 1; end; return 0;
(3)释放锁时关闭 Watch Dog
当调用 unlock()
时,Redisson 会 清理 Watch Dog 任务,避免不必要的续期。
关键代码
java
复制
下载
@Override public void unlock() { try { // 1. 释放锁 get(unlockAsync(Thread.currentThread().getId())); } finally { // 2. 取消 Watch Dog 任务 cancelExpirationRenewal(Thread.currentThread().getId()); } }
3. 关键配置参数
参数 | 默认值 | 说明 |
---|---|---|
lockWatchdogTimeout |
30,000 ms | 锁的默认过期时间(未指定 leaseTime 时生效) |
续期间隔 | 10,000 ms | Watch Dog 的检查间隔(lockWatchdogTimeout / 3 ) |
leaseTime |
-1 | 如果手动指定(如 lock.lock(10, TimeUnit.SECONDS) ),则 不启动 Watch Dog |
4. 看门狗的适用场景
(1)适合使用 Watch Dog 的情况
业务执行时间不确定(如网络请求、复杂计算)。
需要防止锁意外过期(如 GC 停顿导致业务阻塞)。
(2)不适合使用 Watch Dog 的情况
能明确预估业务时间(如
leaseTime=10s
,业务肯定在 5s 内完成)。对锁的持有时间敏感(如严格要求锁 10s 后必须释放)。
5. 与其他方案的对比
机制 | Redisson Watch Dog | SETEX + 自旋重试 | Zookeeper 临时节点 |
---|---|---|---|
自动续期 | ✅ 后台线程定时续期 | ❌ 需业务代码控制 | ❌ 依赖 Session 超时 |
锁安全性 | 高(Lua 脚本原子操作) | 中(依赖业务代码正确性) | 高(CP 系统保证) |
适用场景 | 高并发、业务时间不确定 | 简单场景、短任务 | 强一致性要求严格的场景 |
6. 最佳实践
推荐使用默认 Watch Dog(不指定
leaseTime
),避免锁提前释放。避免滥用锁:锁的粒度要小(如
lock:order:1001
而非lock:order
)。监控 Watch Dog:如果发现续期失败,可能是 Redis 连接问题或锁被强制删除。
总结
Redisson 的 Watch Dog 机制 通过 后台定时任务 + Lua 脚本续期,解决了分布式锁在长业务场景下的 自动保活问题,是生产环境高可用分布式锁的核心保障。
开启新对话