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 的Hashzset等数据结构及publish/subscribe机制,在分布式系统中高效保证了共享资源的互斥访问。

相关推荐
天南星13 分钟前
java-WebSocket在Java生态中的发展历程
java·后端·websocket
工藤学编程39 分钟前
分库分表之实战-sharding-JDBC绑定表配置实战
数据库·分布式·后端·sql·mysql
fmvrj342021 小时前
云原生:数字化转型的核心引擎
后端
码出极致1 小时前
Redisson分布式缓存与数据一致性保障
后端
用户790349033711 小时前
springboot集成redisson实现redis分布式锁
后端
陈随易1 小时前
程序员的新玩具,MoonBit(月兔)编程语言科普
前端·后端·程序员
码出极致1 小时前
Redisson秒杀系统中的分布式锁应用
后端
xiaok2 小时前
@Param注解的作用
java·后端
Sperains2 小时前
async/await和Synchronous的区别
后端
码出极致2 小时前
Redisson可重入锁(RLock)的使用与原理
后端