Skip to content

说说 Redisson 分布式锁的原理? ​

  • 标签:后端、Redis、Redisson

🏛 业务架构师 ​

Redisson 分布式锁值得讲的不是 API,是它替业务兜住的工程细节:

  • 看门狗(watchdog)续期:不指定 leaseTime 时默认锁 30 秒,后台任务每 10 秒(1/3 周期)检查持锁线程还活着就把 TTL 重置——解决"业务没执行完锁过期被别人抢走"的竞态,这是自研锁最容易漏的点;
  • 可重入:锁结构用 Redis hash 存 {锁名: {客户端ID:线程ID: 重入计数}},同线程嵌套加锁计数递增——业务方法嵌套调用时不会自己锁死自己;
  • Lua 保证原子性:加锁(判断+写入+设 TTL)与解锁(验值+计数递减+删除)都是脚本,多步操作不会被打断;
  • 等待与唤醒:拿不到锁订阅该 key 的 PubSub 释放消息,避免无效自旋打爆 CPU;

还有公平锁、读写锁、联锁(MultiLock)、红锁(RedLock,争议方案)等变体。

我的落地观点:

  1. 别自研:分布式锁的坑(续期、误删、原子性)Redisson 都趟过了,自研等于用生产事故重新学习一遍——成熟组件是免费的质量;
  2. 续期不是免死金牌:看门狗管"锁不过期",管不了"业务卡死"——锁内逻辑仍要有限时(超时熔断、快速失败),我要求锁内禁止远程慢调用;
  3. 锁的定位是并发优化:正确性仍靠幂等与唯一约束兜底,Redis 主从切换丢锁的场景永远存在。

组件选型上的这条经验可以推广:把工程资源花在业务差异点,通用问题交给成熟轮子。

🏗 系统架构师 ​

Redisson 分布式锁是对"Redis 锁"的工程化封装,核心机制我拆成三块:

  • 加锁(可重入):不是简单 String,而是 hash 结构:hash key = 锁名,field = 客户端ID:线程ID,value = 重入次数。加锁与续期逻辑用 Lua 脚本保证原子性:锁不存在则 hset + pexpire 并启动看门狗;存在且是当前线程则次数 +1;否则返回剩余 TTL 供客户端做重试(Pub/Sub 订阅解锁消息,避免轮询);
  • 看门狗(watchdog):不指定 leaseTime 时默认锁 30 秒,后台任务每 1/3(10 秒)检查线程还持有就续到 30 秒,解决"业务没执行完锁先过期";客户端宕机则无人续期,锁 30 秒后自动释放;
  • 解锁:Lua 脚本次数 -1,减到 0 才 DEL 并 publish 解锁消息唤醒等待者。

高级形态:公平锁(排队)、读写锁、联锁(MultiLock)、红锁(RedLock 多数派,争议大慎用)。

我的使用姿势:tryLock(waitTime, unit) 带等待上限,锁内代码收敛到关键动作;锁粒度到业务实体 ID;主从架构下极小概率锁互斥被破坏,最终防线永远是业务层幂等与唯一约束,Redisson 是性能优化不是正确性保险。

💻 全栈开发专家 ​

Redisson 的 RLock 是分布式锁的工业级实现,几个机制:

1. 加锁 = Lua 保证原子

lua
-- 简化语义:hset lockkey uuid:threadId 1 + pexpire 30000
-- 用 hash 结构:field 存"客户端ID:线程ID",value 存重入次数 → 天然支持可重入
if redis.call('exists', KEYS[1]) == 0 then
    redis.call('hset', KEYS[1], ARGV[2], 1)
    redis.call('pexpire', KEYS[1], ARGV[1])
    return nil
end
-- 已持有者再次加锁:重入次数 +1,续期

2. 看门狗(watchdog)自动续期:tryLock 不传 leaseTime 才启用,默认 30s,后台任务每 10s(1/3)检查锁还持有就续到 30s——解决"业务没执行完锁过期"。

3. pub/sub 通知:解锁时 publish 消息,等待锁的客户端订阅并随机唤醒(大致公平),避免自旋轮询打爆 Redis。

4. 等待队列 + 可中断:tryLock(waitTime) 支持等待超时和中断,内部用 Redisson 的 Semaphore 实现。

生产注意(我踩过):

  • 锁 key 粒度到业务对象(lock:order:{id}),别锁整个方法;
  • unlock 必须放 finally,且当前线程未持锁调用 unlock 会抛 IllegalMonitorStateException;
  • 主从切换仍有丢锁概率(异步复制),强一致场景要 RedLock 或 etcd;
  • 看门狗依赖客户端存活,进程被 kill -9 后靠 30s 过期兜底,业务要容忍这个窗口。