Redisson 分布式锁的加锁和解锁原理

Redisson 分布式锁的加锁和解锁原理

Redisson 是基于 Redis 实现的 Java 分布式锁框架,其加锁和解锁机制封装了复杂的 Redis 操作细节,同时提供了自动续期、可重入等高级特性。以下从加锁、解锁的核心流程及关键特性原理展开说明:

一、加锁原理

Redisson 的分布式锁基于 Redis 的Hash数据结构和Lua脚本实现,核心是通过原子操作保证锁的互斥性,同时支持可重入和自动续期。

1. 基本加锁流程

  • 数据结构 :锁在 Redis 中以Hash类型存储,key为锁名称(如stock:1001),field为获取锁的线程唯一标识(格式为UUID:线程ID),value为该线程的重入次数。

  • 加锁逻辑(Lua 脚本):

vbnet 复制代码
\-- 若锁不存在,则创建锁并设置重入次数为1,同时设置过期时间


if (redis.call('exists', KEYS\[1]) == 0) then
   redis.call('hset', KEYS\[1], ARGV\[2], 1);
  redis.call('pexpire', KEYS\[1], ARGV\[1]);
  return 1;
end;


\-- 若锁已存在且属于当前线程,则重入次数+1,刷新过期时间


if (redis.call('hexists', KEYS\[1], ARGV\[2]) == 1) then


    redis.call('hincrby', KEYS\[1], ARGV\[2], 1);


    redis.call('pexpire', KEYS\[1], ARGV\[1]);


    return 1;


end;


\-- 若锁已存在但不属于当前线程,返回锁的剩余过期时间


return redis.call('pttl', KEYS\[1]);
  • KEYS[1]:锁名称(如stock:1001)。

  • ARGV[1]:锁的过期时间(默认 30 秒,可自定义)。

  • ARGV[2]:线程唯一标识(UUID:线程ID)。

  • 加锁结果:

    • 返回1:加锁成功(首次获取或重入)。

    • 返回大于 0 的数字:加锁失败,返回当前锁的剩余过期时间(单位:毫秒)。

2. 关键特性原理

  • 可重入性:

    • 同一线程再次加锁时,通过hexists判断锁的field是否为当前线程标识,若是则通过hincrby将重入次数 + 1,并刷新过期时间。

    • 解锁时需递减重入次数,直至次数为 0 才删除锁,保证同一线程的多次加锁 / 解锁操作不会提前释放锁。

  • 自动续期(看门狗机制):

    • 若未指定锁的过期时间,Redisson 会启动一个 "看门狗" 线程(LockWatchdogTimeout默认 30 秒)。

    • 线程每 10 秒(LockWatchdogTimeout/3)检查一次,若锁仍被当前线程持有,则通过pexpire延长锁的过期时间至 30 秒。

    • 确保业务未执行完时,锁不会因过期被释放,避免并发问题。

  • 公平锁机制:

    • 公平锁通过 Redis 的zset结构维护等待队列,每个等待线程对应一个zset的元素(score 为请求时间戳,value 为线程标识)。

    • 加锁时先判断队列中是否有前驱线程,若有则进入等待;无则尝试获取锁,保证按请求顺序分配锁资源,避免线程饥饿。

二、解锁原理

解锁过程同样通过 Lua 脚本保证原子性,核心是校验线程标识、递减重入次数,最终删除锁并通知等待线程。

1. 基本解锁流程

  • 解锁逻辑(Lua 脚本):
lua 复制代码
\-- 若锁不属于当前线程,返回nil(解锁失败)


if (redis.call('hexists', KEYS\[1], ARGV\[3]) == 0) then


    return nil;


end;


\-- 重入次数-1


local counter = redis.call('hincrby', KEYS\[1], ARGV\[3], -1);


\-- 若重入次数>0,刷新过期时间,返回0(未完全释放)


if (counter > 0) then


    redis.call('pexpire', KEYS\[1], ARGV\[2]);


    return 0;


else


    \-- 若重入次数=0,删除锁,返回1(完全释放)


    redis.call('del', KEYS\[1]);


    \-- 发布锁释放通知,唤醒等待线程


    redis.call('publish', KEYS\[2], ARGV\[1]);


    return 1;


end;
  • KEYS[1]:锁名称;KEYS[2]:通知通道名称(如redisson_lock__channel:{lockName})。

  • ARGV[1]:解锁消息;ARGV[2]:过期时间;ARGV[3]:线程唯一标识。

  • 解锁结果:

    • 若锁不属于当前线程:返回nil,解锁失败(避免误删其他线程的锁)。

    • 若重入次数 > 0:刷新过期时间,返回0(仅递减次数,未删除锁)。

    • 若重入次数 = 0:删除锁并通过publish发布通知,返回1(完全释放锁)。

2. 通知机制

  • 未获取到锁的线程会订阅锁对应的 Redis 通道(redisson_lock__channel:{lockName}),并进入阻塞等待。

  • 当锁被完全释放(del操作后),通过publish发送通知,唤醒等待线程重新竞争锁,避免无效的轮询消耗资源。

三、总结

Redisson 分布式锁的核心优势在于:

  1. 原子性:通过 Lua 脚本将加锁 / 解锁的多步 Redis 操作封装为原子操作,避免并发漏洞。

  2. 可靠性:结合 Redis 的过期机制和看门狗续期,防止锁永久占用;支持集群模式,降低单点故障风险。

  3. 易用性 :封装了可重入、公平锁、自动续期等特性,开发者无需关注底层细节,直接通过RLock接口调用即可。

其加锁和解锁过程紧密依赖 Redis 的Hash、zset等数据结构及publish/subscribe机制,在分布式系统中高效保证了共享资源的互斥访问。

相关推荐
明月_清风7 小时前
Muse 登顶 App Store 第一,SDK 直接开源:AI Agent 开始进入下一个阶段
人工智能·后端
hsfxuebao9 小时前
Loop Engineering 保姆级教程 + 项目实战
人工智能·后端
打工仔折腾 AI10 小时前
从 Demo 到生产级 Agent:8 个关键设计机制与 Python 实现拆解
java·jvm·人工智能·后端·python·langchain·ai agent 实战
架构技术专栏10 小时前
交叉熵:AI 怎样给概率预测打分
后端
IT_陈寒12 小时前
SpringBoot自动配置坑了我一把,原来是这样绕过去的
前端·人工智能·后端
要努力啊46912 小时前
用 Codex 加速 Java 开发:从代码生成到测试覆盖的完整实战
后端
dora12 小时前
LangChain4j 新手入门实战教程(Java版)
后端·langchain·agent
蜗牛互联网12 小时前
MongoDB Atlas Agent Engine之后,如何用版本门禁防止陈旧写入
java·数据库·人工智能·后端·mongodb
付威202312 小时前
Rust 生命周期:为什么要有它,实际代码里到底怎么用?
后端
海岳云舟12 小时前
spring使用kafka的三种方式(listener、container、stream)
后端