本文介绍了在SpringBoot应用中基于Redis+Redisson的分布式锁的基本使用,以及Redisson分布式锁的看门狗机制。
1 集成Redis和Redisson
1.1 添加Maven依赖
<!-- redis(RedisTemplate)-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- redisson -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.18.0</version>
</dependency>
本文的SpringBoot版本为2.7.18,对应最新的redisson版本为3.18.0。
redisson和SpringBoot版本的对应关系可参考文章:redisson版本与springboot版本依赖关系
1.2 添加Redis配置
在application.properties配置文件中添加redis配置:
(本文redis为集群部署,可根据自身redis进行配置)
# redis密码
spring.redis.password=redis@password123
# 集群节点
spring.redis.cluster.nodes=7.201.11.221:6379,7.201.11.222:6379,7.201.11.223:6379,7.201.11.224:6379,7.201.11.225:6379,7.201.11.226:6379
spring.redis.cluster.max-redirects=3
# 集群拓扑自适应刷新
spring.redis.lettuce.cluster.refresh.adaptive=true
# 集群拓扑定时刷新周期,单位:毫秒
spring.redis.lettuce.cluster.refresh.period=30000
1.3 配置RedissonClient
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* RedissonConfig
*/
@Configuration
public class RedissonConfig {
// 注入Redis配置
@Autowired
private RedisProperties redisProperties;
/**
* 自定义配置 RedissonClient
*
* @return RedissonClient
*/
@Bean(destroyMethod = "shutdown")
public RedissonClient redissonClient() {
Config config = new Config();
// 集群模式
config.useClusterServers()
.addNodeAddress(redisProperties.getCluster().getNodes().stream().map(node -> "redis://" + node).toArray(String[]::new))
.setPassword(redisProperties.getPassword());
return Redisson.create(config);
}
}
2 Redisson分布式锁的基本使用
lock():
@Service
public class TicketService {
@Resource
private RedissonClient redissonClient;
private void createTicket() {
// 获取锁,"lock:createTicket"为锁住的资源名,用于区分不同锁
RLock lock = redissonClient.getLock("lock:createTicket");
lock.lock(); // 阻塞等待加锁
try {
// 加锁执行的代码
doCreateTicket();
} finally {
lock.unlock(); // 释放锁
}
}
}
tryLock():
// 模拟集群部署时的多实例并发问题
public void runTask() {
// 加锁实现任务的串行,防止同时执行
RLock lock = redissonClient.getLock("lock:taskName");
if (lock.tryLock()) { // 尝试获取锁,获取不到直接结束
try {
doRunTask();
} finally {
lock.unlock(); // 释放锁
}
}
}
3 看门狗机制
此章节包含AI生成的内容
Redisson 的 看门狗机制(Watchdog Mechanism) 是其分布式锁功能中最核心、最强大的特性之一,它解决了传统分布式锁中 “锁过期但业务未执行完” 的经典问题。
✅ 一、问题背景:为什么需要看门狗?
假设你用 Redis 实现了一个简单的分布式锁:
SET lock_key my_instance NX EX 30
- 设置一个 30 秒过期的锁。
- 如果业务逻辑执行时间 超过 30 秒,锁自动过期。
- 此时另一个实例可以获取锁 → 出现 两个实例同时持有锁 → 锁失效!
❌ 问题:锁的过期时间必须预估业务执行时间,保守设置会导致并发降低,设置太短又可能提前释放。
✅ 二、Redisson 看门狗机制(Watchdog)是什么?
Redisson 的看门狗是一个 自动续期机制:
当一个线程成功获取锁后,Redisson 会启动一个后台线程(看门狗),每隔一段时间(默认 1/3 锁超时时间)自动给锁 延长过期时间,只要持有锁的线程还在运行。
🔧 工作流程图解
线程A获取锁(lock())
↓
Redisson 启动看门狗(后台线程)
↓
看门狗每 10 秒检查一次(默认)
↓
如果线程A仍持有锁 → 执行 EXPIRE lock_key 30
↓
锁的 TTL 重新设置为 30 秒
↓
直到线程A调用 unlock() → 看门狗停止,锁被删除
✅ 三、关键参数(默认值)
| 参数 | 值 | 说明 |
|---|---|---|
| 锁默认超时时间(leaseTime) | 30 秒 | lock() 不传参时的 TTL |
| 看门狗检查间隔 | 10 秒 | 30 * 1/3 ≈ 10 秒 |
| 续期命令 | EXPIRE lock_key 30 | 重置 TTL |
✅ 四、代码示例
RedissonClient redisson = Redisson.create(config);
RLock lock = redisson.getLock("my:lock");
// 获取锁(默认 30 秒超时,看门狗自动续期)
lock.lock();
try {
// 执行业务逻辑(可能耗时 1 分钟)
Thread.sleep(60_000);
System.out.println("业务执行完成");
} finally {
// 释放锁 → 看门狗停止,锁被删除
lock.unlock();
}
在这个例子中:
- 即使业务执行了 60 秒,锁也不会提前释放。
- 看门狗会在第 10 秒、20 秒、30 秒、40 秒、50 秒时自动续期。
- 最终
unlock()被调用,锁被删除。
✅ 五、看门狗如何避免续期风暴?
Redisson 使用了 单例看门狗 + 懒启动 策略:
- 多个锁共享同一个看门狗调度器。
- 每个锁的续期任务独立,但调度是高效的。
- 只有持有锁的线程才启动续期任务。
✅ 六、什么情况下看门狗不会工作?
| 场景 | 说明 |
|---|---|
❌ 使用 lock(long, TimeUnit) 并指定 leaseTime 大于 0 | 如lock.lock(10, SECONDS) 会禁用看门狗 |
| ❌ JVM 宕机或线程崩溃 | 看门狗线程也停止 → 锁会在 TTL 后自动释放(安全) |
| ❌ Redis 宕机 | 依赖 Redis 高可用(如哨兵、集群) |
| ❌ 网络分区 | 持有锁的节点失联 → 锁最终过期(遵循 CP 模型) |
✅ 七、总结
| 特性 | 说明 |
|---|---|
| 🎯 核心价值 | 实现“只要线程活着,锁就不会过期” |
| ⚙️ 自动续期 | 每 1/3 TTL 时间自动 EXPIRE |
| 🛡️ 防误删 | 结合 threadId + UUID 唯一标识锁 |
| 💡 无需预估时间 | 业务执行多久都不怕锁过期 |
| ✅ 推荐使用 | lock() 而不是 tryLock() 或带超时的 lock(timeout) |
💬 一句话总结:
Redisson 的看门狗就像一个“自动浇水的花盆”,只要你这朵“花”还活着(线程运行),它就会定时给你“浇水”(续期),防止你“枯死”(锁过期)。
这是 Redisson 分布式锁 高可用、易用、安全 的关键所在。