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。

相关推荐
Gl�ria38 分钟前
Redis 高可用架构对比
数据库·redis
Zenova EdgeOS3 小时前
Python 工业边缘 redis-py 异步实战
开发语言·redis·python
hanchenxing3 小时前
前端异步任务三方案:Celery vs Redis Stream vs BullMQ 实战对比消息队列
前端·数据库·redis·异步任务·方案对比
九皇叔叔8 小时前
从本地事务到分布式事务
java·分布式·分布式事务·cap·base
刃神太酷啦10 小时前
Redis 核心进阶:哨兵、集群、缓存问题与分布式锁详解----《Hello Redis!》(6)
linux·c语言·数据库·c++·redis·分布式·缓存
suweijie76812 小时前
Redis 7.0.2 Docker Compose 部署
redis·docker
爱和冰阔落12 小时前
【Linux】手写日志与固定线程池:任务队列、工作线程和安全退出
linux·运维·c++·redis·安卓
海宇大数据12 小时前
分布式网关架构实战:基于海宇数据公安二要素认证即时版构建自动化司机准入网关
人工智能·分布式·架构·自动化
像风一样自由202013 小时前
29.Redis在大模型应用中有哪些用途缓存会话与限流
数据库·人工智能·redis·缓存·大模型·milvus·智能体
weixin_4604435617 小时前
考试监控大屏为什么不能直接查数据库?WebSocket、Redis与实时数据聚合架构设计
数据库·redis·在线考试系统·培训考试系统·煤矿培训考试系统·煤矿在线考试系统·宏远培训考试系统