在 Java 构建微服务或高并发系统时,Redis 分布式锁几乎是标配。为了提高分布式锁的可靠性,Redis 官方提出了 RedLock(红锁)算法,并推荐在加锁过程中引入看门狗机制,以解决锁过早释放的问题。
本文将详细讲解:
-
为什么在红锁中需要看门狗机制
-
Java 中如何实现看门狗机制(包含 Redisson 和手动实现)
-
实战建议与注意事项
一、红锁中的锁过期问题
🧨 场景
你使用 Redis 设置一个 10 秒的分布式锁:
SET my-lock <UUID> NX PX 10000
但你的业务逻辑执行了 15 秒 —— 锁在中途自动过期,其他客户端误以为锁已释放,并发执行了本不该同时执行的逻辑!
✅ 解决方案:看门狗机制
引入看门狗机制(Watchdog),定期刷新锁的过期时间,只要业务还在运行,就自动延长锁的 TTL,防止锁失效。
二、Redisson 的自动续期机制(内置看门狗)
Redisson 是 Java 中使用 Redis 分布式锁的首选框架,封装了红锁和看门狗机制。
✅ 使用示例:
RLock lock = redissonClient.getLock("my-lock");
lock.lock(); // 默认开启看门狗机制,自动续期
try {
// 执行业务逻辑(不管你多久,锁都不会提前释放)
doSomeLongRunningJob();
} finally {
lock.unlock(); // 手动释放锁
}
🔍 内部原理:
-
默认锁 TTL 为 30 秒
-
Redisson 启动一个定时任务,每 10 秒向 Redis 发送
PEXPIRE
续期 -
如果客户端宕机或线程终止,Redisson 停止续期,Redis 自动删除锁
三、手动实现看门狗机制(不使用 Redisson)
如果你不使用 Redisson,而是用原生 Redis 客户端(如 Jedis、Lettuce),也可以手动实现看门狗。
🧵 手动看门狗 + 锁代码示例(Jedis)
public class RedisLockWithWatchdog {
private static final String LOCK_KEY = "my-lock";
private static final String LOCK_VALUE = UUID.randomUUID().toString();
private static final int TTL_SECONDS = 10;
private static final int WATCHDOG_INTERVAL = 5;
private static volatile boolean running = true;
private static Jedis jedis = new Jedis("localhost", 6379);
public static void main(String[] args) {
if (tryLock()) {
// 启动看门狗线程
Thread watchdog = new Thread(() -> {
while (running) {
try {
TimeUnit.SECONDS.sleep(WATCHDOG_INTERVAL);
String val = jedis.get(LOCK_KEY);
if (LOCK_VALUE.equals(val)) {
jedis.expire(LOCK_KEY, TTL_SECONDS);
System.out.println("🔄 看门狗续期锁");
} else {
break; // 锁已失效或被别人抢了
}
} catch (InterruptedException e) {
break;
}
}
});
watchdog.start();
try {
System.out.println("🚀 执行业务逻辑");
TimeUnit.SECONDS.sleep(20); // 模拟长时间任务
} catch (Exception e) {
e.printStackTrace();
} finally {
unlock();
running = false;
}
}
}
private static boolean tryLock() {
String result = jedis.set(LOCK_KEY, LOCK_VALUE, "NX", "EX", TTL_SECONDS);
return "OK".equals(result);
}
private static void unlock() {
if (LOCK_VALUE.equals(jedis.get(LOCK_KEY))) {
jedis.del(LOCK_KEY);
System.out.println("✅ 锁释放成功");
}
}
}
☑️ 说明:
-
WATCHDOG_INTERVAL
是看门狗执行间隔,建议设为TTL
的一半或 1/3 -
解锁时使用 UUID 验证防止误删他人锁
-
如果进程崩溃,看门狗线程终止,Redis 锁会自动过期
四、注意事项与最佳实践
问题 | 解决建议 |
---|---|
锁被提前释放 | 使用看门狗定期续期 |
Redisson 看门狗间隔 | 可通过 setLockWatchdogTimeout() 自定义 |
客户端宕机 | 看门狗线程停止,Redis 锁自动过期,无需人工清理 |
UUID 锁标识 | 加锁/续期/解锁都要验证 UUID,确保锁归属 |
业务异常 | try-finally 必须保证锁一定释放 |
五、总结
项目 | Redisson 自动看门狗 | 手动实现 |
---|---|---|
简洁性 | ✅ 非常方便 | ❌ 代码复杂 |
安全性 | ✅ 多线程安全 | ⚠ 需小心并发 |
灵活性 | ✅ 支持配置 TTL | ✅ 可自定义逻辑 |
建议使用 | ✅ 推荐生产使用 | 🧪 适合学习实验 |