Redisson 分布式锁实现流程与使用场景

1. Redisson 分布式锁实现流程

0. 什么是分布式锁

在多个服务器(进程)之间,保证同一时刻只有一个能执行某段代码。

类比:公共厕所门锁 → 有人进去锁门,其他人等着,出来下一个人再进。

1. 配置 Redisson 客户端(RedissonConfig.java)

  1. 创建 Config 对象
  2. 指定 Redis 地址和密码(单机/哨兵/集群)
  3. Redisson.create(config) → 返回 RedissonClient Bean

注意:项目中已有此配置,注入 @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();
}

注意事项:

  1. tryLocklock 都对应 unlock
  2. 不释放锁 → 其他线程永远等不到锁 → 死锁
  3. 必须放在 finally 中 → 抛异常也能释放

6. 完整代码示例(一人一单的写法)

RLock redisLock = redissonClient.getLock("lock:order:" + userId);
boolean isLock = redisLock.tryLock();
if (!isLock) {
    log.error("不允许重复下单!");
    return;
}
try {
    // 查订单 + 扣库存 + 创建订单
} finally {
    redisLock.unlock();
}

2. 使用分布式锁的场景

0. 什么时候需要分布式锁

判断标准:是否同时满足以下三个条件

  1. 多线程并发访问同一资源
  2. 部署了多台服务器(集群)
  3. 资源有状态变化(读写操作)

三个条件缺一不可 → 单机用 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 就够)
  • 队列串行消费 → 不需要(天然避免并发)
  • 不需要写数据库,不走定时、秒杀、扣减、判重的业务 → 不需要
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐