Redis 分布式锁:Redisson 集成与业务落地
synchronized/ReentrantLock 只在一台 JVM 内有效。多实例部署时,"扣库存、领券、幂等写"必须用分布式锁。Redis 单线程执行命令,天然适合做锁存储;直接 SETNX 容易踩"没设过期崩溃死锁、忘了续期锁早失效"的坑,Redisson 把加锁、过期、看门狗续期、可重入都封装好了。
集成
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
</dependency>
版本取最新稳定版,starter 与 Boot 版本兼容矩阵以 Redisson 官方文档为准。显式声明客户端最直观(避免依赖其 starter 的配置键差异):
@Configuration
public class RedissonConfig {
@Bean(destroyMethod = "shutdown")
public RedissonClient redissonClient() {
Config cfg = new Config();
cfg.useSingleServer().setAddress("redis://127.0.0.1:6379");
// 哨兵/集群/主从换 useSentinelServers / useClusterServers,参数以官方文档为准
return Redisson.create(cfg);
}
}
业务用法:RLock
@Service
public class StockService {
private final RedissonClient redisson;
public boolean deduct(Long skuId, int num) {
RLock lock = redisson.getLock("stock:lock:" + skuId); // 同一资源 = 同一 key
try {
if (!lock.tryLock(3, TimeUnit.SECONDS)) { // 最多等 3 秒抢锁
return false; // 输出:抢锁失败,业务提示稍后重试
}
return doDeduct(skuId, num); // 临界区:查库存→扣减→写回
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
} finally {
if (lock.isHeldByCurrentThread()) { // 只释放自己持有的锁
lock.unlock();
}
}
}
}
要点:
tryLock(waitTime, leaseTime, unit):waitTime 是抢锁等待上限,leaseTime 是锁自动过期时间;不传 leaseTime 时看门狗每 10 秒自动续期(默认 30 秒过期),业务没跑完锁不会提前消失;显式传了 leaseTime 则到期即释放、不再续期;- 锁可重入:同一线程可再次 lock 同一把锁,计数释放;
- 底层用 Lua 脚本保证"加锁/解锁"原子性,解锁会校验持有者标识(UUID+线程号),不会误删别人的锁。
业务落地建议
- key 设计:
业务域:资源类型:资源id(如order:lock:20250101001),粒度越细并发越高; - 抢锁失败策略:快速失败提示(如上)、短等待重试、或退避排队,按业务容忍度选择;
- 兜底:锁不是银弹——扣库存建议"锁 + 数据库条件更新
update stock set n=n-? where n>=?"双保险; - 监控:记录拿锁等待时间、锁内执行时间,超过阈值告警(多为慢 SQL 或长事务);
- Redisson 还提供公平锁 FairLock、读写锁 ReadWriteLock、信号量 Semaphore 等,适用场景以官方文档为准。
小结:Redisson 的 RLock 提供 tryLock + 看门狗续期 + 可重入 + 原子释放;业务上注意 key 粒度、失败策略与数据库兜底,锁才能安全落地。