分布式锁的深水区:Redisson 原理与脑裂问题解法
分布式锁的深水区:Redisson 原理与脑裂问题解法
Section titled “分布式锁的深水区:Redisson 原理与脑裂问题解法”在单机应用里,用个 ReentrantLock 就能锁住线程。但在微服务里,当 10 台机器同时执行抢单、扣库存逻辑时,我们必须把锁放到一台大家都能看见的公共中间件上。这就是分布式锁。
绝大多数开发者提到分布式锁,第一反应就是:“Redis 嘛,SETNX key value EX 10,搞定!”
如果你在 2026 年的架构评审会上这么说,大概率会被直接淘汰。因为纯手写的 SETNX 充满了致命的漏洞。
1. 为什么手写 SETNX 必死无疑?
Section titled “1. 为什么手写 SETNX 必死无疑?”手写 Redis 分布式锁,通常死于以下三种死状:
死状一:过期时间无法把控(业务没跑完,锁被释放了)
Section titled “死状一:过期时间无法把控(业务没跑完,锁被释放了)”你加了 EX 10(10 秒过期)。结果因为一次严重的 GC 停顿,或者数据库查询很慢,你的业务跑了 12 秒。
到了第 10 秒时,Redis 自动把锁删了。此时,第二台机器趁虚而入拿到了锁。两台机器同时操作同一笔订单,数据彻底污染。
死状二:别人解了你的锁
Section titled “死状二:别人解了你的锁”紧接上面的剧情:第一台机器在第 12 秒终于跑完业务了,它去执行 DEL key 释放锁。但是,此时这个 key 其实是属于刚刚拿到锁的第二台机器的!第一台机器一通瞎操作,把别人的门给砸开了,导致第三台机器又闯了进来。
死状三:非原子操作
Section titled “死状三:非原子操作”有人说,那我释放锁的时候先 GET 一下判断是不是我的锁,然后再 DEL。但这变成了两步操作。在多线程环境下,第一步判断通过,还没来得及删,如果发生线程切换,锁刚好过期,结果你依然可能误删别人的锁。
2. 业界标准:Redisson 与看门狗 (Watchdog)
Section titled “2. 业界标准:Redisson 与看门狗 (Watchdog)”为了彻底解决这些问题,Java 圈的绝对标准变成了 Redisson 客户端。它是怎么做的呢?
Lua 脚本保驾护航(解决死状二和死状三)
Section titled “Lua 脚本保驾护航(解决死状二和死状三)”在 Redisson 中,加锁和解锁的底层全是用 Lua 脚本交给 Redis 执行的。 Redis 在执行 Lua 脚本时,会将其视为一个原子操作。要么全成功,要么全失败,中途绝不允许其他命令插队。 同时,加锁时它会生成一个 UUID 作为 Value(标记这是哪个线程加的锁)。解锁的 Lua 脚本会先验证 UUID,匹配才执行删除,彻底杜绝了误删别人锁的可能。
核心黑科技:看门狗(Watchdog)机制(解决死状一)
Section titled “核心黑科技:看门狗(Watchdog)机制(解决死状一)”如果你用 Redisson 加锁时不指定过期时间:
RLock lock = redisson.getLock("my_lock");// 不传 leaseTime,触发看门狗机制lock.lock();底层发生的故事极其精妙:
- Redisson 默认给你加一个 30 秒 超时的锁。
- 只要你的业务还在跑(当前线程持有锁),后台会有一个 Watchdog 守护线程(定时任务)。
- 它每隔
30 / 3 = 10秒,就向 Redis 发送一段续期 Lua 脚本,把这把锁的存活时间重新拨回到 30 秒(自动续命)! - 当业务代码执行完
lock.unlock()时,主动停止看门狗并删除锁。 - 神来之笔: 如果你的这台服务器突然断电宕机了(或者 JVM 崩溃),看门狗线程也死了,没法续命了。那么最多过 30 秒,这把锁就会被 Redis 自动释放,绝对不会造成死锁。
3. 终极拷问:Redlock 与集群脑裂 (Split-Brain)
Section titled “3. 终极拷问:Redlock 与集群脑裂 (Split-Brain)”Redisson 这么完美,是不是天下无敌了?
有一天,你们公司的 Redis 从单机升级成了 Redis Cluster (主从哨兵架构)。
- 你的应用向 Redis 主节点(Master)成功加上了一把锁。
- 主节点还没来得及把这把锁的数据同步(异步复制)给从节点(Slave),主节点网线被人拔了!
- 哨兵(Sentinel)发现主节点失联,立刻把从节点提拔成了新的主节点。
- 此时,另一个应用来抢锁。因为新的主节点上压根没有之前那把锁的记录,它加锁成功了!
恭喜你,脑裂(Split-Brain)发生了! 同一时间,两台机器在两个脑子里都认为自己拿到了锁,并发惨案再次爆发。
Martin Kleppmann 的质疑与 ZooKeeper 的地位
Section titled “Martin Kleppmann 的质疑与 ZooKeeper 的地位”分布式系统专家 Martin Kleppmann 曾对此提出严厉批评:Redis 的异步复制架构注定了它是一个 AP 系统(保证高可用),它根本不适合做绝对安全的强一致性分布式锁。
如果你是一个银行或者证券公司的核心交易系统,哪怕十万分之一的概率出现两把锁,都会造成真实的金钱损失。
这时候,你必须抛弃 Redis,转投 ZooKeeper (或 Etcd)。
ZooKeeper 的降维打击: ZooKeeper 采用的是基于 Paxos(ZAB 协议)的 CP 架构(保证强一致性)。 当你向 ZK 申请加锁(创建一个临时顺序节点)时,这个请求必须得到集群中过半数(多数派)节点的确认落盘,它才会返回成功给你。
这意味着:哪怕 Leader 节点挂了,新选举出来的 Leader 也一定包含了你刚才加锁的数据。在 ZooKeeper 的字典里,绝对不可能出现锁丢失导致脑裂的问题。
4. 总结:做架构,懂妥协
Section titled “4. 总结:做架构,懂妥协”在 2026 年的技术选型会议上:
- 如果你的业务是“抢个优惠券”、“限制一个用户不能 1 秒内发两遍帖子”,使用 Redis + Redisson(AP架构)。极致的高性能,即便因为极端主从切换脑裂,导致多发了一张券,业务上打个补丁也就糊弄过去了。
- 如果你的业务是“给千万级大客户做清算打款”,请老老实实引入 ZooKeeper(CP架构),并在数据库层面加上**悲观锁(FOR UPDATE)或乐观锁(版本号)**做最后的物理兜底。
没有包治百病的银弹,只有懂得权衡利弊的架构师。