开放问答
Redis 分布式锁看门狗在长 GC 下提前失效:高并发写脏数据如何根治?
该问题的正文、回答与评论。
提问者 林寒山 · 状态 open
分布式锁Redis高并发性能调优JVM
【背景】 我负责一个库存扣减服务:JDK 17 + Spring Boot 3 + Redis 7(Cluster)+ Lua 脚本,核心链路是用 Redis 分布式锁保证「读库存→判断→扣减→回写」的原子性。锁是自己实现的 Redlock 思路,TTL 设 10s,后台起了一个每 3s 检查一次的看门狗线程负责续期(PEXPIRE)。QPS 峰值约 3000,单机部署。
【现象】 上线三个月里出现 5 次「超卖 / 扣成负数」,每次都是同一形态:P50/P99 正常,但偶发某条库存被并发写成负数。抓现场发现,出问题的时间窗口里 JVM 正好有一次约 180ms 的 G1 停顿,看门狗线程在停顿期间没跑,TTL 在续期前就过期了,另一个节点拿到锁,两边同时扣减。
【已尝试的排查与结果】
- 把 TTL 从 10s 拉到 30s、续期间隔从 3s 缩到 1s:超卖频率下降但没消失,说明只是降低概率,没根治;
- 把看门狗从普通线程换成独立线程池 + 心跳:同样在长 GC 时失效;
- 怀疑时钟漂移,给集群各节点做了 NTP 校时,基本一致(误差 <5ms),排除;
- 现在临时方案是把这段逻辑挪到 Lua 里「锁+扣减」一次原子执行,但这样锁的语义和续期有点拧巴,不太放心。
【期望得到的答案形态】
- 分布式锁看门狗在长 GC / 网络抖动下失效,业界有没有比较稳的根治方案(比如 fencing token、把锁和写操作原子化、坚持 Redis 还是直接换 etcd/ZooKeeper)?
- 如果坚持用 Redis,TTL 和续期间隔的取值有没有可参考的经验公式或判据?
- 这种「锁提前释放导致并发写脏数据」的故障,有没有可复现的压测方法能提前暴露它?
评论
暂无评论。