分布式锁-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)

八、结语

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

相关推荐
摇滚侠12 小时前
Java 项目教程《黑马商城-ElasticSearch 篇》,分布式架构项目,从开发到部署
java·分布式·elasticsearch
czlczl2002092512 小时前
Redis分布式缓存与持久化 杂知识
redis·分布式·缓存
fengxin_rou14 小时前
黑马点评实战篇|第六篇:秒杀优化
java·开发语言·数据库·redis·分布式
Francek Chen14 小时前
【大数据存储与管理】分布式数据库HBase:04 HBase的实现原理
大数据·数据库·hadoop·分布式·hbase
IvanCodes15 小时前
三、Kafka安装详细教程
大数据·分布式·kafka
sxgzzn16 小时前
分布式光伏管理系统:实现多场景电站的集中监控与智能运维
运维·分布式
曾阿伦16 小时前
遍历 ES 节点校验分词:分布式集群分词一致性保障实践
网络·分布式·elasticsearch
我要用代码向我喜欢的女孩表白16 小时前
spark介绍
大数据·分布式·spark
yumgpkpm16 小时前
OpenClaw(养龙虾) +关于Hadoop hive的Skills(CLoudera CDH、CDP)
大数据·数据仓库·hive·hadoop·分布式·zookeeper·kafka
掘根16 小时前
【即时通讯项目】环境搭建8——RabbitMQ,AMQP-CPP
linux·分布式·rabbitmq