分布式锁-redission锁重试和WatchDog机制

一、前言:为什么你的分布式锁"有时失效"?

你是否遇到过这些问题:

  • 业务执行了 40 秒,但锁 30 秒就没了,导致超卖?
  • 多个服务同时抢锁,有的直接失败,没有重试?
  • 明明加了锁,却出现并发修改?

根本原因,很可能在于你没用好 Redisson 的两大核心机制

🔹 锁重试(Lock Retry) ------ 智能等待,避免立即失败

🔹 WatchDog(看门狗) ------ 自动续期,防止业务超时丢锁

本文将带你彻底掌握这两大机制的工作原理、使用方式与避坑指南


二、机制一:锁重试(Lock Retry)------ 智能等待,不轻易放弃

场景问题:

当多个线程/服务同时请求同一把锁时,未获得锁的一方该如何处理

  • 直接报错? → 用户体验差
  • 无限等待? → 线程阻塞,资源耗尽

Redisson 的解决方案:

提供 带超时的 tryLock() ,支持最大等待时间 + 持有时间

API 示例:
java 复制代码
RLock lock = redisson.getLock("inventory:lock");

// 最多等待 3 秒获取锁,获得后最多持有 10 秒
boolean isLocked = lock.tryLock(3, 10, TimeUnit.SECONDS);

if (isLocked) {
    try {
        // 扣减库存等业务逻辑
        deductStock();
    } finally {
        lock.unlock();
    }
} else {
    throw new BusinessException("系统繁忙,请稍后再试");
}

🔍 底层原理:

  1. 客户端尝试加锁(执行 Lua 脚本)
  2. 若失败,订阅 Redis 的解锁频道(Pub/Sub)
  3. 启动定时任务,每隔 1/3 持有时间(如 10s/3 ≈ 3.3s)重试
  4. 一旦持有者释放锁,会发布消息,唤醒所有等待者竞争

✅ 优势:避免轮询浪费 CPU,实现高效等待


三、机制二:WatchDog(看门狗)------ 自动续期,永不丢锁

经典问题重现:

java 复制代码
lock.lock(); // 默认 TTL = 30 秒
Thread.sleep(40_000); // 业务耗时 40 秒
// 此时锁已过期!其他线程可进入 → 并发安全破坏!

WatchDog 如何解决?

只要你不 unlock,锁就永远不会过期!

工作流程:
  1. 调用无参 lock() 时,不设置 TTL
  2. 同时启动后台线程(WatchDog)
  3. 每隔 10 秒internalLockLeaseTime / 3 = 30000 / 3)检查:
    • 如果当前线程仍持有该锁 → 执行 PEXPIRE myLock 30000
  4. 调用 unlock() 后,取消 WatchDog 定时任务

📊 时间线示例:

复制代码
t=0s   → 加锁,启动 WatchDog
t=10s  → WatchDog 续期(TTL 重置为 30s)
t=20s  → 再次续期
t=25s  → unlock() 被调用
t=25s+ → WatchDog 停止,锁被删除

💡 关键点 :WatchDog 只在 无参 lock() 时启用!

若使用 lock(10, SECONDS)不会启动 WatchDog,10 秒后强制过期!


四、源码级解析:WatchDog 如何实现?

核心类:org.redisson.RedissonLock

1. 加锁时注册续期任务:
java 复制代码
void scheduleExpirationRenewal(...) {
    // 将锁信息存入本地 Map
    ExpirationEntry entry = new ExpirationEntry();
    expirationRenewalMap.put(lockName, entry);
    
    // 启动定时任务
    Timeout task = commandExecutor.getConnectionManager().newTimeout(
        timeout -> {
            // 发送 PEXPIRE 命令续期
            renewExpirationAsync(threadId);
            // 递归调度下一次
            scheduleExpirationRenewal(...);
        },
        internalLockLeaseTime / 3, // 默认 10 秒
        TimeUnit.MILLISECONDS
    );
    entry.addTimeout(task);
}
2. 解锁时取消任务:
java 复制代码
void cancelExpirationRenewal(...) {
    ExpirationEntry entry = expirationRenewalMap.remove(lockName);
    if (entry != null) {
        for (Timeout timeout : entry.getTimeouts()) {
            timeout.cancel(); // 取消所有定时任务
        }
    }
}

设计精妙:通过本地 Map + Netty Timeout 实现高效管理


五、锁重试 + WatchDog 联合使用示例

java 复制代码
public void seckill(Long productId, Long userId) {
    RLock lock = redisson.getLock("seckill:product:" + productId);
    
    try {
        // 最多等待 2 秒抢锁,抢到后由 WatchDog 自动续期
        if (lock.tryLock(2, TimeUnit.SECONDS)) {
            // 业务可能耗时较长(如调用风控、支付)
            doSeckillBusiness(productId, userId);
        } else {
            log.warn("用户 {} 抢购商品 {} 失败:锁等待超时", userId, productId);
            throw new BusinessException("活动太火爆,请稍后再试");
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        throw new RuntimeException(e);
    } finally {
        if (lock.isHeldByCurrentThread()) {
            lock.unlock(); // 安全解锁
        }
    }
}

最佳实践

  • 抢锁阶段 :用 tryLock(waitTime) 避免无限阻塞
  • 持有阶段:依赖 WatchDog 自动续期,无需担心业务超时

六、常见误区与避坑指南

❌ 误区 1:认为 lock(30, SECONDS) 会自动续期

真相只有无参 lock() 才启动 WatchDog
lock(30, SECONDS) 表示"最多持有 30 秒",到期强制释放。

❌ 误区 2:在异步线程中 unlock()

java 复制代码
lock.lock();
CompletableFuture.runAsync(() -> {
    lock.unlock(); // ❌ threadId 不匹配,无法解锁!
});

正解 :解锁必须在加锁的同一线程中进行

❌ 误区 3:忘记判断是否持有锁就 unlock()

风险 :可能抛出异常或误删他人锁
建议 :使用 lock.isHeldByCurrentThread() 判断


七、性能与可靠性对比

方案 是否支持重试 是否自动续期 适用场景
手写 Redis SET NX 简单低并发
Redisson lock() ✅(阻塞) 通用高可靠
Redisson tryLock(w, h) ✅(超时) ✅(仅无参 holdTime) 用户交互型业务

📌 推荐

  • 后台任务 → 用 lock()
  • Web 请求 → 用 tryLock(waitTime, leaseTime)

八、结语

感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!

相关推荐
naierfengdian28 分钟前
分布式风电助力乡村能源转型的技术路径
分布式·能源
hhzz2 小时前
《深度学习框架PyTorch入门与实践》系列:09-分布式与并行训练之DataParallel、DDP与Horovod
pytorch·分布式·深度学习
城管不管17 小时前
重生——第九次面试2026.8.13某车一面
分布式·ai·面试·职场和发展·rabbitmq
ly768919 小时前
分布式一致性算法详解:从 2PC、3PC 到 Paxos、Raft、ZAB
分布式·算法
珠***格19 小时前
通信链路全打通:西格电力四可装置的 4G/5G + 加密认证技术详解
大数据·数据库·分布式·5g·架构·能源
ACP广源盛1392462567321 小时前
WAIC2026 国产算力浪潮@ACP#YLB3118 在算力矩阵中的存储定位与落地场景
大数据·人工智能·分布式·单片机·嵌入式硬件
蜀道山老天师1 天前
ZABBIX分布式监控Proxy应用实践
分布式·zabbix
liudashuang20171 天前
Kafka Producer 隐藏深坑:Sender 线程自阻塞(自死锁)导致 BufferExhaustedException 完整复盘
java·分布式·kafka·linq
rustfs1 天前
GitLab 如何与 RustFS 集成?
分布式·rust·gitlab
Francek Chen1 天前
【大数据处理与分析】数据仓库Hive:02 数据湖
大数据·数据仓库·hive·hadoop·分布式·数据分析