Redisson 分布式锁原理 普通/不解决主从宕机
为了让业务在多线程、分布式环境下不出现超卖、数据错乱、并发安全问题,手写的 Redis 锁代码,存在不可重入、无法重试、锁超时释放三大缺陷。为了解决这些问题,引入了 Redisson 分布式锁。它底层采用 Lua 脚本保证原子性,我们只需要直接调用 API,就能实现安全、可靠、高性能的分布式锁。
一定要看,学习须知
这里学习的 Redisson 分布式锁,只解决了自定义 Redis 锁三大基础问题:可重入、锁重试、锁超时自动续约。但是该锁只连接单个 Redis 节点,没有考虑集群环境。企业级项目中 Redis 一般都是主从集群、多节点部署,因为主从数据同步存在延迟,一旦主节点宕机、从节点上位,就会出现锁丢失、主从一致性安全问题。
所以为了解决这个集群多节点的安全漏洞,就需要使用
Redisson 连锁 MultiLock。 (下个笔记讲)
自定义锁的三大问题
- 抢锁失败无法重试:当原生锁抢锁失败的时候,流程会直接终止
- 锁超时提前释放:业务耗时过长、线程阻塞卡顿,自定义锁会释放,其他线程会乘机抢占锁,引发并发数据安全问题。
- 锁不可重入:同一个线程在嵌套业务逻辑下多次加锁,会直接被阻塞拦截,无法适配复杂嵌套逻辑。
为了解决这些问题我们选择 Redisson 分布式锁
一、怎么解决三个问题的原理
1.Redisson 可重入锁底层实现
基础能力 只需要采用 Redis Hash 结构实现
重入锁有啥用: 在同一线程嵌套调用多层加锁逻辑时,不会阻塞、不会死锁,适配复杂业务嵌套调用,提升代码健壮性。
为什么 Redisson 可以重入锁:
自定义锁,结构是 string 当加锁第二次的时候会卡死,所以不能重入(嵌套),只有简单 lock:ok 当嵌套的时候会发现上一层已经有锁了,下一层无法嵌套就会卡死。
lock:ok
为什么 Redisson 采用 Hash 不会卡死:
key:锁名称
field:当前线程ID
value:加锁次数
在加锁的时候会,看锁存不存在,是不是自己的线程,如果是就次数加一 然后继续嵌套
2.高性能锁失败重试机制
自定义: 没抢到 → 不停疯狂重试、一直消耗性能
Redisson 高性能重试:没抢到 → 休眠等待别人释放锁发信号 → 才唤醒重试
底层实现:基于 Redis 发布与订阅(Pub/Sub) 机制线程抢锁失败后订阅解锁频道,进入阻塞休眠;当其他线程解锁时,发布消息,唤醒等待线程再重试抢锁。
3.看门狗 WatchDog 自动续约机制
防止业务没跑完、锁提前过期
为什么需要看门狗:
锁默认 30 秒过期, 如果你的业务执行很慢、阻塞、卡顿,超过 30 秒 ,过期了锁会过期释放,其他线程抢锁不安全。
我们必须自动延长锁的过期时间,这就叫做看门狗(想起来有个游戏叫做看门狗,有人玩过吗?)
底层:
- 线程成功拿到锁
- Redisson 自动开启一个后台定时任务
- 默认锁有效期 30s,看门狗每 10s自动续期一次
- 去 Redis 校验:这把锁是不是当前线程持有的
- 是自己的锁 → 把锁过期时间重新刷新回 30 秒
- 无限循环、递归执行,一直续命
二、Redisson 加锁与解锁完整流程 底层实现流程
Redisson 底层全程用 Lua 脚本 保证原子性;
三大核心:Hash 可重入 + 发布订阅重试 + WatchDog 看门狗续约
一、加锁完整流程
执行 Lua 脚本尝试抢锁
情况分支:
① 锁不存在:创建 Hash,记录当前线程 ID,重入次数 = 1
② 锁存在、是当前自己线程:重入次数 +1,刷新过期时间(可重入)
③ 锁存在、是别人线程:抢锁失败,返回锁剩余过期时间 TTL
抢锁成功:
如果没有手动指定锁超时时间(leaseTime=-1)
自动开启 WatchDog 看门狗 定时续约任务
每 10 秒刷新锁为 30 秒有效期,无限续命
抢锁失败:
不会死循环空转(不浪费 CPU)
订阅 Redis 锁释放的 发布订阅消息
进入阻塞等待,限时等待(自己设置的最大等待时间)
收到别人解锁通知 / 等待时间没耗尽 → 再次循环重试抢锁
等待时间耗尽仍没抢到 → 返回加锁失败
二、解锁完整流程
业务执行完毕,调用 unlock (),执行解锁 Lua 脚本
先校验:这把锁是不是当前线程的
不是自己的锁:直接禁止解锁,防止乱删锁
是自己的锁:重入次数 -1
分支判断:
① 重入次数 > 0不删除锁,只刷新过期时间,锁继续生效
② 重入次数 = 0真正删除锁 Key
锁彻底删除后:
通过 publish 发布锁释放消息
唤醒所有正在等待抢锁的其他线程
最后一步:
关闭、取消当前锁绑定的 看门狗定时续约任务
停止自动续期,释放后台线程资源
三、如何实现
1. 引依赖
项目里加入 Redisson 的 starter 依赖
<!-- Redisson 分布式锁依赖 -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
</dependency>
2. 连接 Redis
配置 Redis 地址、端口,让 Redisson 连上 Redis
3. 注入 RedissonClient 在代码里面写
直接用 @Autowired 拿到客户端
4. 调用 API 加锁、解锁
你可以按照业务需求选
lock () 或 tryLock ()业务写完 unlock ()
代码实现:
无限等待模式(lock.lock ()) 任务不能丢、必须执行、只能来一次的业务
@Autowired // 注入 RedissonClient
private RedissonClient redissonClient;
public void doImportantTask() {
RLock lock = redissonClient.getLock("task:lock");
try {
// 一直等,拿不到锁绝不罢休
lock.lock();
// 这里写核心业务
System.out.println("执行必须完成的任务");
} finally {
// 必须解锁
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
限时等待模式(tryLock) 用户请求、秒杀、接口、能重试的普通业务
@Autowired
private RedissonClient redissonClient;
public void createOrder() {
RLock lock = redissonClient.getLock("order:lock");
try {
// 最多等 3 秒,抢不到直接放弃
boolean locked = lock.tryLock(3, TimeUnit.SECONDS);
if (!locked) {
System.out.println("抢锁失败,系统繁忙");
return;
}
// 执行业务(下单、扣库存)
System.out.println("抢锁成功,开始创建订单");
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}更多推荐




所有评论(0)