Redisson分布式锁
分布式锁用于控制多个服务实例同时操作同一份共享资源。例如多个订单服务实例同时处理同一个订单,如果没有并发控制,就可能重复扣库存、重复支付、重复发券。
Redisson 分布式锁基于 Redis 实现,并通过 Lua 脚本、锁标识、自动续期等机制提高可用性。
基本流程
flowchart TD
A[线程尝试获取锁] --> B{锁不存在?}
B -->|是| C[写入锁和线程标识]
B -->|否| D{是否当前线程持有?}
D -->|是| E[重入次数加 1]
D -->|否| F[等待锁释放]
C --> G[执行业务]
E --> G
G --> H[finally 中释放锁]可重入锁
可重入锁指同一个线程已经拿到锁后,可以再次获取同一把锁而不会被自己阻塞。
典型场景:
RLock lock = redissonClient.getLock("order:lock:1001");
try {
if (lock.tryLock(3, 30, TimeUnit.SECONDS)) {
// 执行业务
}
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}Redisson 会记录当前锁被同一线程重入的次数。每次 unlock 会让次数减一,直到次数为 0 才真正删除锁。
看门狗续期
flowchart TD
A["业务线程在Redis加锁成功"] --> B["WatchDog注册续期任务"]
B --> C["业务仍持锁且客户端存活"]
C --> D["WatchDog按周期校验并续期"]
D --> C
C --> E["业务完成后校验所有权并释放"]如果没有指定锁过期时间,Redisson 默认会通过看门狗续期。这样可以避免业务执行时间超过默认锁时间导致锁提前释放。
但这不代表锁内可以执行无限长任务。锁持有时间越长,系统吞吐越低。
联锁(RedissonMultiLock)
联锁是把多把锁合成一把锁使用。只有所有锁都获取成功,整体才算加锁成功。
适合一个业务操作必须同时锁住多个资源的场景,例如同时锁订单和库存。
flowchart TD
A[尝试获取锁 A] --> B{成功?}
B -->|否| X[失败返回]
B -->|是| C[尝试获取锁 B]
C --> D{成功?}
D -->|否| E[释放锁 A 后失败返回]
D -->|是| F[执行业务]
F --> G[释放锁 A 和锁 B]注意:联锁会增加失败概率和等待时间,使用前要确认是否真的必须同时锁多个资源。
红锁(RedissonRedLock)
红锁是 Redis 分布式锁的多节点多数派思路:客户端向多个相互独立的 Redis 节点加锁,只要多数节点加锁成功,并且耗时没有超过锁有效期,就认为加锁成功。
flowchart TD
A["客户端生成 token"] --> B["多个独立 Redis 节点加锁"]
B --> C["统计成功数量"]
C --> D{"是否达到多数派"}
D -- "是" --> E["校验加锁耗时"]
D -- "否" --> F["释放部分锁并失败"]
E --> G{"耗时是否小于 TTL"}
G -- "是" --> H["获得红锁"]
G -- "否" --> F红锁用于降低单个 Redis 节点故障带来的锁安全问题,但它不是强一致共识算法。普通业务中,单 Redis 锁或 Redisson RLock 加上幂等、状态机和补偿通常更常见;需要理解红锁完整流程、适用边界、Java Demo 和面试回答时,看 Redis 红锁 Redlock。
更多扩展能力
Redisson 除了 RLock、MultiLock 和红锁,还会在项目里用到公平锁、读写锁、信号量、可过期信号量、闭锁等能力。它们不是为了“显得高级”,而是分别解决顺序等待、读多写少、限制并发数、异常自动回收许可、跨实例流程等待等问题。
flowchart TD
A["Redisson 同步能力"] --> B["RLock 可重入锁"]
A --> C["FairLock 公平锁"]
A --> D["ReadWriteLock 读写锁"]
A --> E["Semaphore 信号量"]
A --> F["CountDownLatch 闭锁"]
A --> G["Redlock 多节点多数派"]完整原理、适用场景和代码 demo 看 分布式锁扩展点。
常见风险
锁过期但业务没执行完
如果锁提前过期,其他线程可能拿到锁并发执行。解决方式:
- 使用看门狗续期。
- 设置足够合理的 leaseTime。
- 缩短锁内业务耗时。
误删别人的锁
释放锁时必须校验锁归属。Redisson 内部会用线程标识判断当前线程是否持有锁,避免删除其他线程的锁。
Redis 故障
Redis 锁依赖 Redis 可用性。对资金、库存这类强一致场景,要结合数据库约束、状态机、幂等表、补偿任务一起设计。
使用建议
- 加锁粒度尽量小,例如按订单 ID 加锁,不要全局加锁。
- 锁内只放必须串行的代码。
- 解锁放在
finally。 - 业务本身仍然要幂等,不能完全依赖锁。
- 高并发热点资源要考虑队列化或状态机,避免大量线程争抢锁。
