Redis 分布式锁与 Redisson:从抢券超卖讲到 WatchDog、可重入和 RedLock

分布式锁最适合用真实业务来理解。抢券场景:多个用户同时抢优惠券,系统要保证库存不能被扣成负数。

如果代码部署在单机上,synchronized 可能够用;但服务一旦集群部署到多个 Tomcat 实例,本地锁就只能锁住当前 JVM,挡不住其他节点上的线程。

这就是 Redis 分布式锁的使用场景:集群环境下,让多个服务实例争抢同一把锁。

一、为什么抢券会超卖?

普通抢券逻辑大概是:

java 复制代码
Integer num = redisTemplate.opsForValue().get("num");
if (num == null || num <= 0) {
    throw new RuntimeException("优惠券已抢完");
}
num = num - 1;
redisTemplate.opsForValue().set("num", num);

两个线程可能同时读到库存 1,然后都判断库存充足,最终都扣减成功。
Redis 库存 线程2 线程1 Redis 库存 线程2 线程1 查询库存 = 1 查询库存 = 1 扣减为 0 扣减为 0

问题不在 Redis,而在"查询库存"和"扣减库存"不是一个不可被打断的整体。

二、SETNX 实现分布式锁

Redis 实现分布式锁主要依赖 SETNX,也就是 SET if Not eXists:key 不存在时才能设置成功。

现在更推荐用一条 SET 命令同时完成加锁和设置过期时间:

text 复制代码
SET lock:coupon threadId NX EX 10

含义是:只有 lock:coupon 不存在时才设置成功,并且 10 秒后自动过期。
成功
失败
尝试获取锁
SET NX EX 是否成功
执行业务
释放锁
等待或重试

释放锁时也要小心,必须确认这把锁是自己的,再删除。否则线程 A 的锁过期后,线程 B 获取了新锁,线程 A 再执行删除就可能误删线程 B 的锁。

所以释放锁通常要用 Lua 脚本保证"判断锁标识 + 删除锁"是原子操作。

三、Redisson 做了哪些封装?

Redisson 是 Redis 分布式锁的成熟封装。课件里提到几个重点:

  1. 底层基于 SETNX 和 Lua 脚本保证原子性。
  2. 使用 WatchDog 给锁自动续期。
  3. 使用 Hash 结构记录线程标识和重入次数,实现可重入。
  4. 支持锁重试等待。

四、WatchDog:锁怎么自动续期?

如果业务执行时间超过锁过期时间,锁提前释放,就会有并发问题。Redisson 的 WatchDog 会在持有锁的线程还没执行完时自动续期。
Redis WatchDog 业务线程 Redis WatchDog 业务线程 加锁成功 定期检查锁仍被当前线程持有 续期锁过期时间 业务完成后释放锁

默认情况下,WatchDog 会周期性续期,避免业务还没执行完锁就过期。

五、可重入锁:为什么用 Hash?

可重入指的是同一个线程已经拿到锁后,可以再次获取同一把锁。

Redisson 会用 Hash 结构记录线程 id 和重入次数:

text 复制代码
heimalock
  thread-1 -> 2

同一个线程再次加锁时,重入次数加 1;释放锁时,重入次数减 1;只有减到 0 才真正删除锁。
线程第一次加锁
重入次数 = 1
线程再次加锁
重入次数 = 2
释放一次,重入次数 = 1
再次释放,删除锁

六、主从一致性和 RedLock

Redis 主从架构下,分布式锁还有一个风险:线程在 Master 上加锁成功,但锁还没同步到 Slave,Master 就宕机了。Slave 被提升为新 Master 后,其他线程可能又获取到同一把锁。

RedLock 的思路是:不要只在一个 Redis 实例上加锁,而是在多个独立 Redis 节点上加锁,只要超过 n / 2 + 1 个节点成功,就认为加锁成功。

但课件也提醒:RedLock 性能较差,不是默认推荐方案。如果业务真的要求极强一致,建议考虑 ZooKeeper 这类更适合强一致锁的方案。

七、面试回答模板

可以这样答:

我们项目里在抢券、幂等性或集群定时任务场景用过分布式锁。Redis 分布式锁底层主要依赖 SET NX EX 加锁,并用 Lua 脚本保证释放锁时判断和删除的原子性。实际项目里一般使用 Redisson,它封装了 WatchDog 自动续期、可重入锁、锁重试等能力。Redisson 的可重入基于 Hash 结构记录线程标识和重入次数。至于主从一致性问题,RedLock 可以缓解但性能较差,如果业务必须强一致,应该考虑 ZooKeeper。

相关推荐
一只普通的码农1 小时前
Filebeat 在windows环境部署并结合kafka使用
分布式·kafka
shark-chili2 小时前
基于claude code的redis慢查询指令复刻实践
数据库·redis·缓存
難釋懷2 小时前
Redis网络模型-IO多路复用-select方式
网络·redis·bootstrap
未若君雅裁2 小时前
Redis 集群方案详解:主从复制、哨兵、脑裂、分片集群和哈希槽
redis·哈希算法
IronMurphy3 小时前
Redis拷打第二讲
数据库·redis·缓存
手握风云-3 小时前
Redis:不只是缓存那么简单(八)
redis·缓存
covco3 小时前
AI 原生营销矩阵系统:分布式架构设计与核心模块实现
人工智能·分布式·矩阵
phltxy3 小时前
Redis Set:原理、命令与实战场景详解
数据库·redis·缓存
Java识堂4 小时前
Kafka 如何保证消息的可靠性投递?
分布式·kafka