【深度复盘】Redis 分布式锁:从 SETNX 到 Redisson 看门狗的架构权衡

【深度复盘】Redis 分布式锁:从 SETNX 到 Redisson 看门狗的架构权衡

标签: Redis 分布式锁 Redisson 并发编程 架构设计
复盘时间: 2026-01-25

一、 为什么需要分布式锁?(演进之路)

在单机多线程环境下,我们使用 synchronizedReentrantLock;但在微服务/集群环境下,本地锁无法锁住其他 JVM 进程,因此需要引入第三方组件(Redis/Zookeeper)来实现全局锁。

1. 原始阶段:SETNX

  • 命令: SETNX lock:key value (Set if Not Exists)
  • 致命缺陷: 如果机器宕机或程序崩溃,锁未释放(死锁)。
  • 补丁: 引入过期时间(TTL)。

2. 进阶阶段:原子性与误删

  • 原子性问题: SETNXEXPIRE 是两步操作,非原子。
    • 解决: 使用 Redis 2.6+ 复合命令:SET key value EX 10 NX
  • 误删问题: 线程 A 卡顿,锁过期自动释放;线程 B 上锁;线程 A 醒来执行 DEL,把线程 B 的锁删了。
    • 解决: UUID + Lua 脚本
    • Value 存入唯一 ID(如 UUID),删除前判断 if (get(key) == uuid) { del(key) },必须通过 Lua 保证判断和删除的原子性。

3. 终极阶段:续期问题

  • 痛点: 业务没跑完,锁过期了怎么办?
  • 解决: 引入 守护线程(Daemon Thread) 自动续期。这就是 Redisson 的核心价值。

二、 Redisson 核心机制深度解析

1. 看门狗 (WatchDog) 机制

  • 触发条件: 加锁时不指定 LeaseTime(租约时间)。
  • 原理:
    • 默认租约时间 (lockWatchdogTimeout) 为 30秒
    • Redisson 启动一个后台线程,每隔 10秒 (1/3 租约时间) 检查一次,如果主线程还持有锁,就重置过期时间为 30秒。
  • 停止条件: 显式解锁 (unlock) 或 客户端宕机(看门狗线程随之死亡,30秒后锁自动过期)。

2. 架构哲学:Safety vs Liveness(面试杀手锏)

问题: 如果业务代码死循环,看门狗一直续期导致锁无法释放(死锁),是不是不如"固定过期时间"好?

架构决策:

  • 场景 A:固定过期时间(无看门狗)
    • 业务死循环 -> 锁过期释放 -> 其他线程进入 -> 并发修改同一数据。
    • 后果: 脏写 (Dirty Write) 。数据被污染,账平不上了。(Safety 丢失)
  • 场景 B:看门狗自动续期
    • 业务死循环 -> 锁一直存在 -> 其他线程拿不到锁(阻塞)。
    • 后果: 服务停摆 (Deadlock) 。业务卡住,但数据是安全的。(Liveness 丢失)

结论: 对于金融/交易等核心业务,数据一致性(Safety)高于可用性(Liveness) 。宁可人工介入重启服务解决死锁,也不能容忍数据被脏写。因此,看门狗机制是核心业务的"保护神"

3. 锁等待机制:Pub/Sub vs 自旋

  • SETNX: 失败返回 0,客户端通常需要 while(true) 自旋重试,消耗 CPU。
  • Redisson: 利用 Redis 的 Pub/Sub(发布订阅)
    • 获取锁失败时,订阅锁的释放频道,然后挂起当前线程(Semaphore)。
    • 收到释放消息后,唤醒线程再次尝试。
    • 优势: 无效等待时不耗 CPU,性能更优。

三、 Redisson API 实战指南

在代码中,根据业务场景选择不同的锁策略:

1. 死磕模式(核心业务)

适用于必须执行且不能并发的任务(如每日跑批、资金结算)。

java 复制代码
RLock lock = redisson.getLock("myLock");
lock.lock(); // 激活看门狗,默认30s,自动续期
try {
    // 业务逻辑(哪怕跑1小时也不怕)
} finally {
    lock.unlock();
}

2. 优雅等待模式(高并发 Web 业务)

适用于大多数互联网请求,防止请求堆积。

java 复制代码
// 尝试等待10秒,拿到锁后,锁在30秒后强制过期(无看门狗!)
// 参数:waitTime, leaseTime, unit
boolean res = lock.tryLock(10, 30, TimeUnit.SECONDS); 
if (res) {
   try {
     // 业务处理
   } finally {
     lock.unlock();
   }
} else {
   throw new RuntimeException("系统繁忙,请稍后再试"); // 快速失败
}

注意: 一旦指定了 leaseTime(第二个参数),看门狗机制失效

3. 快速失败模式(防抖/非核心)

适用于防止用户重复点击。

java 复制代码
if (lock.tryLock()) { // 不等待,拿不到立刻返回 false
    try { ... } finally { lock.unlock(); }
} else {
    return;
}

四、 总结(记忆钩子)

  1. 原子性: 加锁用 SET ... NX EX,解锁用 Lua 脚本。
  2. 误删: Value 必须存 UUID 做身份验证。
  3. 看门狗: 解决"业务没跑完锁过期"的问题。不传 leaseTime 时触发。
  4. 取舍: 核心业务选看门狗(保数据),高并发业务选固定过期时间(保服务)。
相关推荐
人间凡尔赛1 分钟前
AI-Native 云原生架构:2026 年从容器编排到智能体编排的范式革命
后端·云原生·架构
LONGZETECH17 分钟前
新能源汽车充电设备装配与调试仿真教学软件 技术架构与核心实现解析
大数据·算法·3d·unity·架构·汽车
昕光xg19 分钟前
Istio笔记04-基于Jaeger的分布式链路追踪
笔记·分布式·istio
星创易联24 分钟前
从T-Box到5G车载网关:高阶自动驾驶通信架构演进
5g·架构·自动驾驶
Ai拆代码的曹操37 分钟前
opencode CLI 源码拆解:yargs + effectCmd 双层架构如何管理 20+ 子命令
架构·ai编程·opencode·源码拆解
熊猫钓鱼>_>38 分钟前
ArkUI 动画实战:从理论到交互,构建流畅的鸿蒙动效体验
华为·架构·交互·harmonyos·arkts·arkui·native
流星白龙1 小时前
【Redis】1.Redis特性
数据库·redis·缓存
咩咩啃树皮10 小时前
第40篇:Vue3组件化开发精讲——组件拆分、复用、父子通信、工程化架构
java·前端·架构
CS创新实验室12 小时前
量子计算机的操作系统:原理、实践与经典架构之对比
架构·操作系统·量子计算