news 2026/5/3 16:59:21

你以为写个 SETNX 就是分布式锁了?揭秘死锁危机与 Redisson 续期魔法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
你以为写个 SETNX 就是分布式锁了?揭秘死锁危机与 Redisson 续期魔法


案发场景:
晚上 8 点,电商秒杀系统上线。为了防止商品超卖,你在扣库存的代码外层加了一把 Redis 分布式锁。
你的青铜代码:

// 1. 加锁,设置 10 秒过期时间(防止服务器宕机死锁)redis.setIfAbsent("lock:sku:10086","1",10,TimeUnit.SECONDS);try{// 2. 执行扣库存业务逻辑doBusiness();}finally{// 3. 释放锁redis.delete("lock:sku:10086");}

灾难降临(锁失效与误删并发):
假设此时发生了网络抖动,或者 JVM 触发了一次长达 5 秒的 Full GC。导致你的doBusiness()耗时达到了15 秒

  • 第 10 秒:锁过期了,Redis 自动释放了锁。
  • 第 11 秒:线程 B 以为没人加锁,成功抢到了锁,开始执行业务。此时,线程 A 和 线程 B 都在扣同一个商品的库存!超卖发生!
  • 第 15 秒:线程 A 终于执行完了。它来到了finally块,执行delete。可怕的事情发生了:线程 A 把线程 B 刚刚加上的锁给删掉了!
  • 第 16 秒:线程 C 又趁虚而入……

整个系统瞬间变成了“裸奔”状态,你的SETNX成了一个彻头彻尾的笑话。


1. 锁的自我修养:如何防止“误删别人的锁”?

为了解决案发场景中的“误删”问题,架构师们迈出了进阶的第一步:身份验证。

加锁的时候,不能随便塞个"1"进去,必须塞入当前线程的唯一标识 (UUID + ThreadId)
释放锁的时候,不能闭着眼睛DEL,必须先查一下:“这把锁还是不是我的?是我的我才删。”

为了保证“查询并删除”这两个动作的绝对原子性,我们必须使用上一期讲过的Lua 脚本

-- Lua 脚本:安全的释放锁ifredis.call("GET",KEYS[1])==ARGV[1]thenreturnredis.call("DEL",KEYS[1])elsereturn0end

解决了误删问题,那“业务没执行完,锁却提前过期了”的问题怎么解决?


2. 终极矛盾:TTL 的生死困局

过期时间 (TTL) 设多久永远是个两难的哲学问题。

  • 设短了(比如 5 秒):稍微遇到个慢 SQL,业务还没跑完,锁就没了,并发安全被击穿。
  • 设长了(比如 5 分钟):如果拿到锁的服务器突然停电宕机了,这把锁要等 5 分钟才会自动释放。这 5 分钟内,所有其他用户的请求全部被阻塞,业务陷入死寂。

我们需要一种动态伸缩的机制:
“只要我的业务还在执行,你就一直帮我把锁的时间延长;只要我的服务器宕机了,你就不再延长,让它立刻过期释放。”

这,就是名震江湖的Redisson WatchDog (看门狗) 机制


3. 破局者:Redisson WatchDog 底层魔法

Redisson 是目前最强大的 Java Redis 客户端之一,它彻底封装了分布式锁的复杂底层逻辑。

当我们调用redissonClient.getLock("myLock").lock()时,一场精妙的“续租”游戏就在后台悄悄上演了。

看门狗的工作原理:
  1. 默认租期:加锁成功时,Redisson 默认给这把锁设置30 秒的 TTL。
  2. 启动定时任务 (TimerTask):Redisson 会在后台线程开启一个定时任务(这只狗)。
  3. 三分之一续期法:这只看门狗会每隔10 秒(即默认 30 秒的 1/3)醒来一次。
  4. 探活与续租:它会去检查一下,拿着这把锁的线程是不是还活着、业务是不是还在跑?如果是,它就会向 Redis 发送一段 Lua 脚本,重新把这把锁的 TTL 刷新回 30 秒!
  5. 安全释放:
    • 如果业务正常执行完,调用了unlock(),Redisson 会主动删除锁,并且把看门狗任务取消掉。
    • 极限宕机测试:如果拿着锁的 Java 进程突然被kill -9杀死了,或者断电了。那么看门狗线程也会跟着一起死掉!没人再给锁续期了。经过最多 30 秒,Redis 里的锁就会自然过期释放,完美避免死锁!

4. 代码落地:Spring Boot + Redisson 实战与惊天巨坑

接入 Redisson 非常简单,但里面隐藏着一个无数高级研发都会踩中的巨坑

importorg.redisson.api.RLock;importorg.redisson.api.RedissonClient;importorg.springframework.stereotype.Service;importjava.util.concurrent.TimeUnit;@ServicepublicclassOrderService{privatefinalRedissonClientredissonClient;publicOrderService(RedissonClientredissonClient){this.redissonClient=redissonClient;}publicvoidcreateOrder(){// 1. 获取锁对象 (只是拿到引用,还没去 Redis 加锁)RLocklock=redissonClient.getLock("lock:order:create");// --- 错误写法 (巨坑警告!) ---// lock.lock(10, TimeUnit.SECONDS);// 很多人习惯性地加个 10 秒参数,以为更安全。// 殊不知!只要你传了 leaseTime 参数,Redisson 就会认为你自己能掌控过期时间,// 从而 彻底关闭 WatchDog 看门狗机制! 导致锁在 10 秒后必定失效!try{// --- 正确写法 (召唤看门狗) ---// 只有无参的 lock(),或者 tryLock 的 leaseTime 传 -1 时,// 才会触发 WatchDog 机制!默认锁 30 秒,并每 10 秒自动续期。lock.lock();// 或者带有等待时间,但不传 leaseTime (传 -1) 的 tryLock// boolean isLocked = lock.tryLock(5, -1, TimeUnit.SECONDS);System.out.println("成功获取锁,看门狗已就位,开始执行长达 40 秒的超慢业务...");Thread.sleep(40000);}catch(Exceptione){e.printStackTrace();}finally{// 安全释放锁 (Redisson 内部会判断这把锁是不是当前线程的,不会误删)if(lock.isLocked()&&lock.isHeldByCurrentThread()){lock.unlock();}}}}

5. 进阶探讨:分布式锁的终极梦魇 —— Redlock (红锁)

有了 Redisson 看门狗,你的业务在单机 Redis 上已经 100% 安全了。
但是!如果你的 Redis 部署的是主从集群 (Master-Slave)呢?

CAP 定理的无情制裁:

  1. 线程 A 在 Master 节点加锁成功。
  2. 还没来得及异步同步给 Slave 节点,Master 机器主板烧了!
  3. 哨兵机制 (Sentinel) 立刻将 Slave 提升为新的 Master。
  4. 线程 B 过来加锁,发现新的 Master 上没有锁,加锁成功!
  5. 灾难再次发生:线程 A 和 线程 B 同时拥有了锁!

为了解决这个极端问题,Redis 作者 Antirez 提出了Redlock (红锁)算法。
其核心思想是:不搞主从复制了。直接部署 5 台独立的 Redis Master 机器。客户端加锁时,并发向这 5 台机器发送命令。只要**超过半数(3台)**加锁成功,才算真正拿到了锁。

架构师的权衡:要不要上 Redlock?
在工业界,Redlock 引发了极其激烈的争论(著名的 Martin vs Antirez 世纪辩论)。
绝大多数互联网大厂的最终结论是:不推荐使用 Redlock。

  • 它的运维成本极高(需要 5 台独立 Master)。
  • 它严重依赖服务器时间的同步(NTP 时钟跳跃会导致红锁失效)。
  • 终极妥协:如果你的业务对数据一致性要求极其变态(比如金融级核心账本),你不应该用 Redis 做锁,你应该去用 ZooKeeper(基于 CP 模型,强一致性)或数据库级悲观锁。Redis(基于 AP 模型)天生就是为了高性能而生的,容忍极小概率的主从切换锁丢失,换取极致的吞吐量,这才是它的归宿。

总结

分布式锁的演进,是一场与并发、网络、宕机时间进行极限博弈的战争。

从裸奔的SETNX,到带过期时间的SET EX NX;从 Lua 脚本防误删,再到 Redisson WatchDog 的自动续期守护。我们穷尽了工程学的智慧。

最后请记住这个铁律:使用 Redisson 时,如果你不懂底层原理,千万不要画蛇添足地给lock()方法传过期时间参数。相信那只默默无闻的看门狗,它比你更懂什么时候该放手。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/15 13:47:48

技术博客的“长尾效应”:写什么文章最容易吸引大厂 Hiring Manager?

在北美求职极度内卷的当下,很多技术方向的留学生开始意识到“建立个人技术影响力”的重要性。于是,大家纷纷搭起个人博客,或者在各大技术社区注册账号,试图用输出倒逼输入。 然而,坚持写了几个月后,很多人发…

作者头像 李华
网站建设 2026/4/16 5:42:12

告别数据拥堵:手把手教你用BLE L2CAP的Credit流控优化自定义信道传输

告别数据拥堵:手把手教你用BLE L2CAP的Credit流控优化自定义信道传输 当你的智能手环需要传输长达2MB的固件升级包时,传统GATT信道会像早高峰的地铁一样陷入瘫痪——每秒仅能传输几百字节,且频繁出现数据丢失。这正是我们团队去年开发工业级可…

作者头像 李华
网站建设 2026/4/15 21:57:16

一文教你怎么将程序结果发送到微信

一、前言因为工作需要,平时经常跑一些程序,一跑就是几小时,甚至几天。而我又不想总干坐着,也不想时时刻刻盯着这一件事。所以我想有没有办法将结果直接发送给微信呢?答案是肯定的。二、pushplus首先前往pushplus的官网…

作者头像 李华
网站建设 2026/4/16 5:42:16

3分钟让Figma秒变中文界面:设计师的终极汉化解决方案

3分钟让Figma秒变中文界面:设计师的终极汉化解决方案 【免费下载链接】figmaCN 中文 Figma 插件,设计师人工翻译校验 项目地址: https://gitcode.com/gh_mirrors/fi/figmaCN 还在为Figma的英文界面而头疼吗?每次设计时都要在工具和翻译…

作者头像 李华
网站建设 2026/5/2 20:53:12

终极QQ空间备份指南:如何永久保存你的青春记忆

终极QQ空间备份指南:如何永久保存你的青春记忆 【免费下载链接】QZoneExport QQ空间导出助手,用于备份QQ空间的说说、日志、私密日记、相册、视频、留言板、QQ好友、收藏夹、分享、最近访客为文件,便于迁移与保存 项目地址: https://gitcod…

作者头像 李华
网站建设 2026/4/15 12:29:40

阶段零:评估指标

评估指标:准确率不是万能的当“考试分数”骗了你——揭秘类别不平衡下的真实模型表现一、开篇故事:一个让人困惑的“优等生” 假设你是一名公共卫生官员,任务是为一种罕见但极其严重的病毒开发快速检测方法。这种病毒每10000人中仅有1人感染。…

作者头像 李华