Redisson 分布式锁实现流程与使用场景
·
Redisson 分布式锁实现流程与使用场景
1. Redisson 分布式锁实现流程
0. 什么是分布式锁
在多个服务器(进程)之间,保证同一时刻只有一个能执行某段代码。
类比:公共厕所门锁 → 有人进去锁门,其他人等着,出来下一个人再进。
1. 配置 Redisson 客户端(RedissonConfig.java)
- 创建
Config对象 - 指定 Redis 地址和密码(单机/哨兵/集群)
Redisson.create(config)→ 返回RedissonClientBean
注意:项目中已有此配置,注入
@Resource RedissonClient即可使用。
2. 获取锁对象
RLock lock = redissonClient.getLock("lock:业务:资源ID");
锁的 key 粒度决定了并发能力:
| 锁类型 | Key 示例 | 效果 |
|---|---|---|
| 全局锁 | lock:all |
❌ 全串行,性能极差 |
| 用户锁 | lock:order:{id} |
✅ 同用户串行,不同用户并行(一人一单) |
| 资源锁 | lock:stock:{id} |
✅ 同资源串行,不同资源并行(库存扣减) |
3. 获取锁(三种方式)
方式 A:tryLock() — 非阻塞,立即返回
获取不到就放弃,适合"抢不到就告诉用户不行"的场景。
boolean isLock = lock.tryLock();
if (!isLock) return;
方式 B:tryLock(waitTime, unit) — 阻塞等待,不续期
最多等 waitTime 秒,等不到就放弃。
if (lock.tryLock(10, TimeUnit.SECONDS)) {
// ...
}
方式 C:lock() — 阻塞等待,自动续期(看门狗)
- 默认 30 秒租期
- 看门狗每 10 秒检查一次
- 线程还在执行 → 续期到新 30 秒
- 线程已崩溃 → 不续期,最多 30 秒后自动释放
4. 看门狗机制(WatchDog)
这是 Redisson 和手写 SETNX 的最大区别:
| 方案 | 机制 | 风险 |
|---|---|---|
手写 SETNX |
估计业务 1 秒跑完 → 设超时 10 秒 | 万一跑了 15 秒 → 锁提前丢了 |
Redisson lock() |
默认 30 秒,每 10 秒检查线程存活 | 存活就续期,崩溃则最长 30 秒自动释放,不会死锁 |
5. 执行业务逻辑(临界区)
try {
// 在锁的保护下执行业务
// 查询订单 → 扣减库存 → 创建订单
} finally {
// 释放锁(必须放 finally!)
lock.unlock();
}
注意事项:
tryLock和lock都对应unlock- 不释放锁 → 其他线程永远等不到锁 → 死锁
- 必须放在
finally中 → 抛异常也能释放
6. 完整代码示例(一人一单的写法)
RLock redisLock = redissonClient.getLock("lock:order:" + userId);
boolean isLock = redisLock.tryLock();
if (!isLock) {
log.error("不允许重复下单!");
return;
}
try {
// 查订单 + 扣库存 + 创建订单
} finally {
redisLock.unlock();
}
2. 使用分布式锁的场景
0. 什么时候需要分布式锁
判断标准:是否同时满足以下三个条件
- 多线程并发访问同一资源
- 部署了多台服务器(集群)
- 资源有状态变化(读写操作)
三个条件缺一不可 → 单机用
synchronized即可,不需要分布式锁。
1. 秒杀/抢购 — 一人一单 / 库存扣减
- 场景:双11,100 万人抢 1000 件商品
- 问题:用户 A 同时发两个请求 → 都查到库存 > 0 → 都扣减成功 → 超卖
- 解法:
lock("seckill:stock:" + skuId)→ 查库存 → 扣减 →unlock
2. 缓存击穿/缓存重建 — 热点 key 过期时只让一个人查 DB
- 场景:热点商品缓存过期,100 个请求同时打到 DB
- 问题:100 个请求都去查 DB → DB 瞬间打满
- 解法:
lock("lock:shop:" + id)→ 查 DB → 重建缓存 →unlock
3. 分布式定时任务 — 防止重复执行
- 场景:每天凌晨 3 点定时结算,部署了 5 台服务器
- 问题:5 台服务器同时执行结算 → 重复扣款
- 解法:
lock("cron:dailySettle")→ 执行任务 →unlock - 效果:抢到锁的执行,没抢到的跳过
4. 幂等性控制 — 防止重复提交
- 场景:用户支付时网络抖动,前端重发了 3 次请求
- 问题:3 次请求都创建了订单 → 重复支付
- 解法:
lock("order:create:" + orderId)→ 判重 → 创建 →unlock
5. 分布式事务中的资源锁定
- 场景:A 给 B 转账,余额扣减和加钱在不同服务
- 问题:两笔同时操作同个账号 → 余额被并发修改
- 解法:
lock("account:" + userId)→ 扣余额 → 加余额 →unlock
6. 分布式 ID 生成(递增序列号)
- 场景:多个服务生成订单号,要求全局唯一递增
- 问题:各服务各自生成 → 可能重复或乱序
- 解法:
lock("id:generator")→INCR→ 生成序列号 →unlock
注:实际 Redis
INCR本身原子,但一旦涉及"先查再设"就需要锁。
7. 分布式的资源调度(节点选举/主从切换)
- 场景:5 台服务器做实时推荐,只需要 1 台做模型训练
- 问题:5 台都训练 → 浪费算力且结果冲突
- 解法:
lock("leader:training")→ 谁抢到谁训练,其他跳过
什么时候不需要分布式锁
- 只读操作 → 不需要(查数据不会改变状态)
- 单机部署 → 不需要(
synchronized/ JUC 就够) - 队列串行消费 → 不需要(天然避免并发)
- 不需要写数据库,不走定时、秒杀、扣减、判重的业务 → 不需要
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)