Redis 分布式锁进阶学习文档

0 阅读9分钟

Redis 分布式锁的三层演进

  1. 不可重入 Redis 分布式锁
  2. 可重入的 Redis 分布式锁
  3. Redisson 的 MultiLock

课程里对这三层的总结非常明确:
不可重入锁依赖 setnx 的互斥性、ex 的超时释放和线程标识校验;可重入锁则通过 hash 结构记录线程标识和重入次数,再配合 watchDog 续期信号量/PubSub 重试等待;但 Redis 主从异步复制仍可能导致锁丢失,所以又引出了 MultiLock,要求在多个独立 Redis 节点上都拿到锁才算成功。


Redis 分布式锁进阶学习文档

1. 这一节在整个秒杀模块里的位置

你现在已经从“单机一人一单”走到了“集群下必须用分布式锁”。
而这张图讲的不是“为什么要锁”,而是:

Redis 分布式锁写出来以后,它本身还会继续升级。

课程目录里也明确把这一段拆成了:

  • Redis 分布式锁实现
  • 锁误删问题
  • 原子性问题
  • Redisson
  • Redisson 可重入锁原理
  • Redisson 锁重试和 WatchDog
  • Redisson 的 MultiLock

也就是说,这一节是在带你理解:

自己手写的简易 Redis 锁够不够?
不够的话,Redisson 到底帮你补了什么?


2. 第一层:不可重入 Redis 分布式锁

2.1 它的核心原理

课程里总结得很直接:

  • 利用 setnx 的互斥性
  • 利用 ex 避免死锁
  • 释放锁时判断线程标识

更早的基础版 Redis 锁思路也是一样的:

  • 获取锁:SET lock thread1 NX EX 10
  • 释放锁:删除 key
  • 获取时设置超时时间,避免服务宕机后永远不释放

你可以把它翻译成业务话:

  • NX:别人没加锁,我才能加
  • EX:就算线程挂了,锁也会自动过期
  • 线程标识:解锁时先确认这把锁是不是自己的

2.2 它解决了什么

它解决的是最基础的分布式互斥问题:

  • 多个 JVM 可以看见同一把锁
  • 同一时刻只有一个线程能执行临界区代码
  • 能防止服务异常后锁永远不释放

2.3 它的缺陷

课件已经直接列出来了:

  • 不可重入
  • 无法重试
  • 锁超时失效

PDF 里也进一步解释了这些问题:
基于 setnx 的锁会有 重入问题、不可重试、超时释放带来的安全隐患,而且主从环境下还会有一致性问题。


3. 什么叫“不可重入”

这个概念你一定要吃透。

3.1 重入是什么意思

“重入”指的是:

同一个线程已经拿到了锁,进入方法 A;
方法 A 里面又调用了方法 B;
方法 B 也想拿同一把锁;
这时如果还能成功,就叫可重入。

Java 里的 synchronizedLock 都是可重入的。课程 PDF 里明确指出:
可重入锁的意义是防止死锁

3.2 不可重入会有什么问题

假设同一个线程已经持有锁,又去尝试获取同一把锁:

  • 如果锁系统只知道“这把锁存在”
  • 却不知道“持有者还是我自己”
  • 那它就会把自己也拦住

这就容易出现“自己锁自己”的死锁风险。


4. 第二层:可重入的 Redis 分布式锁

4.1 课程给出的原理

这张图里已经写得很清楚:

  • 利用 hash 结构
  • 记录 线程标识
  • 记录 重入次数
  • 利用 watchDog 延长锁时间
  • 利用 信号量 / PubSub 控制锁重试等待

4.2 为什么用 hash

PDF 里明确说了,Redisson 在分布式锁中采用 hash 结构 来存储锁:

  • 大 key 表示“这把锁”
  • 小 key 表示“哪个线程持有”
  • value 记录“重入次数”

你可以这样理解:

lock:order
   └── uuid:threadId -> 2

意思是:

  • lock:order 这把锁存在
  • uuid:threadId 这个线程持有它
  • 当前已经重入了 2 次

4.3 可重入锁的加锁逻辑

课件图示的流程本质是:

  1. 先判断锁是否存在
  2. 如果不存在,直接加锁,记录当前线程,计数设为 1
  3. 如果存在,再判断持有者是不是自己
  4. 如果是自己,计数加 1
  5. 如果不是自己,获取失败或等待重试

4.4 可重入锁的解锁逻辑

解锁不是直接删 key,而是:

  1. 判断当前线程是不是持有者
  2. 如果不是,不能删
  3. 如果是,重入次数减 1
  4. 如果减完还大于 0,说明还没真正释放完,只重置过期时间
  5. 只有减到 0,才真正删除锁

所以可重入锁的关键不是“多次 set”,而是:

记录同一线程重入了多少次,按次数逐步释放。


5. WatchDog 是干什么的

这是可重入锁进阶里最重要的点之一。

课程里明确说:
Redisson 用 watchDog 来做 超时续约,会每隔一段时间重置锁的过期时间。

5.1 为什么需要它

因为基础版 Redis 锁虽然加了过期时间,能避免死锁,但会带来新问题:

如果业务执行时间比锁 TTL 还长,锁会提前过期。
这时候别的线程可能趁机拿到锁,导致并发安全问题。

PDF 里把这个问题称为“超时释放带来的安全隐患”。

5.2 WatchDog 的作用

WatchDog 就像“自动续费”:

  • 线程拿到锁后
  • 业务还没做完
  • 看门狗就定期把锁的过期时间往后延

PDF 还给了源码级说明:
它会在一定周期内调用续期逻辑,线程还活着就继续续约;线程挂了就不再续约,锁最终自然过期释放。

所以你可以记成一句话:

WatchDog 解决的是“业务没执行完,锁先过期”的问题。


6. 可重试是什么意思

基础版锁通常是“抢一次就结束”,拿不到就直接失败。
但课程里说,可重入 Redis 锁还支持:

  • 等待
  • 重试
  • 收到释放信号后再抢

这就是为什么 Redisson 比手写 setnx 锁更完整:

  • 不是单纯 try once
  • 而是有等待和唤醒机制
  • 更接近 Java Lock 的使用体验

7. 可重入 Redis 分布式锁的缺陷

虽然它已经比手写锁强很多,但课件仍然给出一个关键缺陷:

Redis 宕机会引起锁失效问题。

7.1 为什么会这样

PDF 解释得很清楚:

  • 你把锁写到 Redis 主节点
  • 主节点还没来得及同步给从节点
  • 主节点就挂了
  • 哨兵把某个从节点提升为新主
  • 但新主里根本没有这把锁的信息

结果就是:

锁丢了。

这不是代码写错,而是 Redis 主从异步复制 的一致性边界问题。


8. 第三层:Redisson 的 MultiLock

8.1 它为什么出现

正是为了补 Redis 主从复制下“锁可能丢失”的问题,课程里才引出了 MultiLock

8.2 它的原理

课件原话就是:

多个独立的 Redis 节点,必须在所有节点都获取重入锁,才算获取锁成功。

PDF 也明确解释了:

  • 不再依赖“一个主从集群里的主节点”
  • 而是在多个彼此独立的 Redis 节点上分别加锁
  • 只有全部成功,才算最终成功
  • 只要有一个节点拿不到,就不算加锁成功

你可以把它理解成:

单点锁 = 一票通过
MultiLock = 全票通过

8.3 它解决了什么

它解决的是:

  • 某个 Redis 主节点突然宕机
  • 锁尚未同步
  • 锁信息丢失

因为 MultiLock 把锁分散写到多个独立节点,只要不是所有节点同时丢锁,就能大幅提高可靠性。


9. MultiLock 的缺点

课件里已经给出结论:

  • 运维成本高
  • 实现复杂

这很好理解:

  • Redis 节点更多
  • 加锁要写多次
  • 失败回滚更复杂
  • 网络开销也更高

所以它不是默认首选,而是更强调 可靠性优先 的锁方案。


10. 这三层你要怎么对比着学

第一层:不可重入 Redis 锁

适合你理解 Redis 锁的最小实现:

  • set nx ex
  • 线程标识
  • 解锁校验

但是问题多:

  • 不可重入
  • 不能优雅重试
  • TTL 到了可能提前失锁

第二层:可重入 Redis 锁 / Redisson Lock

这是企业里更常用的完整方案:

  • hash 记录线程和重入次数
  • watchDog 自动续期
  • 支持等待和重试

但在 Redis 主从切换时,仍可能锁丢失。

第三层:MultiLock

进一步提升可靠性:

  • 多节点全部加锁成功才算成功
  • 避免单个 Redis 主从切换导致锁失效

但代价是更重、更复杂。


11. 放到黑马点评项目里怎么理解

你现在要把它和秒杀的一人一单联系起来理解:

  • 一开始你是用 JVM 锁解决单机一人一单
  • 集群后 JVM 锁失效,改用 Redis 分布式锁
  • 手写 Redis 锁能跑,但能力不完整
  • 最后课程引导你用 Redisson,把可重入、重试、续期这些能力补全
  • 再进一步讲主从一致性问题,引出 MultiLock

所以这不是单独知识点,而是:

秒杀业务逼着锁机制一步步升级。


12. 面试表达

你可以直接按这个思路回答:

Redis 分布式锁最基础的实现是基于 set nx ex,利用 nx 实现互斥,ex 防止死锁,再结合线程标识避免误删别人的锁。但这种方案是不可重入的,也不支持等待重试,而且如果业务执行时间超过锁 TTL,会出现锁提前失效的问题。Redisson 在此基础上实现了可重入锁:用 hash 结构记录线程标识和重入次数,用 watchDog 机制自动续期,用等待和唤醒机制支持锁重试。不过在 Redis 主从架构下,如果主节点写入锁后尚未同步就宕机,锁信息可能丢失。为了解决这个问题,Redisson 还提供了 MultiLock,需要在多个独立 Redis 节点上都获取到锁才算成功,从而提高分布式锁的可靠性。


13. 你现在最该记住的 5 句话

  1. 基础 Redis 锁靠 set nx ex 实现互斥和超时释放。
  2. 不可重入锁的问题是同一线程再次进锁代码时可能把自己卡死。
  3. Redisson 用 hash 记录线程标识和重入次数,实现可重入。
  4. WatchDog 解决的是“业务没执行完,锁先过期”的问题。
  5. MultiLock 解决的是 Redis 主从切换时锁可能丢失的问题。