本文已收录在Github,关注我,紧跟本系列专栏文章,咱们下篇再续!
-
🚀 魔都架构师 | 全网30W技术追随者
-
🔧 大厂分布式系统/数据中台实战专家
-
🏆 主导交易系统百万级流量调优 & 车联网平台架构
-
🧠 AIGC应用开发先行者 | 区块链落地实践者
-
🌍 以技术驱动创新,我们的征途是改变世界!
-
👉 实战干货:编程严选网
0 前言
除了“持有锁的进程崩溃、未释放锁”这一经典场景,还会因以下容易被忽视的问题,导致系统进入类似死锁的阻塞状态:
1 客户端本地时钟漂移
若加锁客户端的系统时间被人为或自动回拨,可能导致锁的本地过期时间计算错误,客户端误以为锁已过期而提前续期或释放,其他客户端趁机抢占,造成多客户端同时持有同一把锁,后续释放时又互相覆盖,最终谁都无法正常完成业务,表现得像死锁。
解决方案
启用 NTP 严格同步,或使用 monotonic 时间源;
2 锁续期(看门狗)异常
Redisson“看门狗”机制依赖后台线程周期性地为锁续期。若业务线程因 GC、CPU 调度或网络抖动被长时间挂起,看门狗线程也随之暂停,锁在 Redis 端真正过期;业务恢复后却仍认为锁有效,继续持有,导致后续竞争者无法获取锁,形成阻塞。
解决方案
限制业务逻辑执行时间,设置合理的 watchdog 超时上限。
3 主从切换期间的锁丢失
Redis主从异步复制下:
- 主节点加锁成功后宕机
- 从节点升为主但尚未收到该锁的写入
新客户端在新【主节点】上再次加锁成功,两个客户端同时持有同一把锁;当它们都尝试释放锁时,可能互相删除对方的键,造成业务逻辑混乱,后续请求因锁状态不一致而持续等待。
解决方案
启用 Redis 哨兵或集群模式,结合 RedLock 多数派策略。
4 Lua 脚本执行超时
用 Lua 脚本保证“判断-删除”原子性时,若脚本因数据量过大或逻辑复杂执行时间超过 Redis 的 lua-time-limit,Redis 会中断脚本并记录 EVALSHA 失败,但客户端可能未收到失败信号,继续认为锁已释放;其他客户端因键仍存在而无法加锁,系统出现“假死”状态。
解决方案
拆分复杂脚本,缩短执行时间,或改用 pipeline 分批执行。