当前位置: 首页 > news >正文

Redisson MultiLock 原理

1. 什么是 MultiLock

Redisson 的 MultiLock(多重锁)是一种分布式锁机制,它允许将多个独立的RLock实例组合成一个逻辑上的锁。当用户需要对多个资源同时加锁时,MultiLock 可以保证这些资源要么全部被锁定,要么全部不被锁定,从而避免部分锁定导致的死锁或数据不一致问题。

2. 核心机制

MultiLock 本身并不是一把新的 Redis 锁,而是一个对多个子锁(RLock)的统一管理器。其加锁过程如下:

  • 顺序加锁:按传入顺序依次尝试获取每一个子锁。
  • 全量成功:只有所有子锁都获取成功,MultiLock 才认为加锁成功。
  • 失败回滚:如果在获取某个子锁时失败,会立即释放已经获取的所有子锁,恢复到加锁前的状态。
  • 异步非阻塞:底层使用 Redisson 的异步非阻塞模型,通过 Lua 脚本在 Redis 服务端保证原子性操作。

3. MultiLock 的特性

  • 原子性操作:通过 Lua 脚本确保加锁和解锁的原子性,避免部分成功或失败。
  • 可重入性:支持同一个线程多次加锁,每次解锁只减少持有计数,计数归零时才真正释放锁。
  • 超时机制:支持设置锁的自动释放时间,防止死锁。
  • 公平锁与非公平锁:根据底层RLock的配置,可以支持公平或非公平锁模式。

4. 代码示例

以下是一个使用 MultiLock 的典型 Java 示例:

RLock lock1 = redisson.getLock("lock1"); RLock lock2 = redisson.getLock("lock2"); RLock lock3 = redisson.getLock("lock3"); // 创建 MultiLock RLock multiLock = redisson.getMultiLock(lock1, lock2, lock3); // 尝试加锁,设置等待时间和锁自动释放时间 boolean isLocked = multiLock.tryLock(10, 30, TimeUnit.SECONDS); if (isLocked) { try { // 执行业务逻辑 } finally { multiLock.unlock(); } }

5. 底层 Lua 脚本

加锁和解锁的核心逻辑由 Lua 脚本在 Redis 服务端执行。以下是加锁的简化逻辑:

-- 参数:KEYS[1..n] 为锁的 key,ARGV[1] 为超时时间,ARGV[2] 为线程标识 local failedLocks = {} for i, key in ipairs(KEYS) do if redis.call('exists', key) == 0 then redis.call('hset', key, ARGV[2], 1) redis.call('pexpire', key, ARGV[1]) else table.insert(failedLocks, key) end end if #failedLocks > 0 then -- 释放已获取的锁,进行回滚 for i, key in ipairs(KEYS) do if not table.contains(failedLocks, key) then redis.call('del', key) end end return 0 end return 1

进一步刨析里面的trylock()源码:代码来源于https://cyborg2077.github.io/2022/10/22/RedisPractice/#Redisson%E9%94%81%E7%9A%84MutiLock%E5%8E%9F%E7%90%86

public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException { long newLeaseTime = -1L; //如果传入了释放时间 if (leaseTime != -1L) { //再判断一下是否有等待时间 if (waitTime == -1L) { //如果没传等待时间,不重试,则只获得一次 newLeaseTime = unit.toMillis(leaseTime); } else { //想要重试,耗时较久,万一释放时间小于等待时间,则会有问题,所以这里将等待时间乘以二 newLeaseTime = unit.toMillis(waitTime) * 2L; } } //获取当前时间 long time = System.currentTimeMillis(); //剩余等待时间 long remainTime = -1L; if (waitTime != -1L) { remainTime = unit.toMillis(waitTime); } //锁等待时间,与剩余等待时间一样 long lockWaitTime = this.calcLockWaitTime(remainTime); //锁失败的限制,源码返回是的0 int failedLocksLimit = this.failedLocksLimit(); //已经获取成功的锁 List<RLock> acquiredLocks = new ArrayList(this.locks.size()); //迭代器,用于遍历 ListIterator<RLock> iterator = this.locks.listIterator(); while(iterator.hasNext()) { RLock lock = (RLock)iterator.next(); boolean lockAcquired; try { //没有等待时间和释放时间,调用空参的tryLock if (waitTime == -1L && leaseTime == -1L) { lockAcquired = lock.tryLock(); } else { //否则调用带参的tryLock long awaitTime = Math.min(lockWaitTime, remainTime); lockAcquired = lock.tryLock(awaitTime, newLeaseTime, TimeUnit.MILLISECONDS); } } catch (RedisResponseTimeoutException var21) { this.unlockInner(Arrays.asList(lock)); lockAcquired = false; } catch (Exception var22) { lockAcquired = false; } //判断获取锁是否成功 if (lockAcquired) { //成功则将锁放入成功锁的集合 acquiredLocks.add(lock); } else { //如果获取锁失败 //判断当前锁的数量,减去成功获取锁的数量,如果为0,则所有锁都成功获取,跳出循环 if (this.locks.size() - acquiredLocks.size() == this.failedLocksLimit()) { break; } //否则将拿到的锁都释放掉 if (failedLocksLimit == 0) { this.unlockInner(acquiredLocks); //如果等待时间为-1,则不想重试,直接返回false if (waitTime == -1L) { return false; } failedLocksLimit = this.failedLocksLimit(); //将已经拿到的锁都清空 acquiredLocks.clear(); //将迭代器往前迭代,相当于重置指针,放到第一个然后重试获取锁 while(iterator.hasPrevious()) { iterator.previous(); } } else { --failedLocksLimit; } } //如果剩余时间不为-1,很充足 if (remainTime != -1L) { //计算现在剩余时间 remainTime -= System.currentTimeMillis() - time; time = System.currentTimeMillis(); //如果剩余时间为负数,则获取锁超时了 if (remainTime <= 0L) { //将之前已经获取到的锁释放掉,并返回false this.unlockInner(acquiredLocks); //联锁成功的条件是:每一把锁都必须成功获取,一把锁失败,则都失败 return false; } } } //如果设置了锁的有效期,因为没把锁的获取时间都不一样,所以剩余过期时间不一样,这里就给时间重新赋值 if (leaseTime != -1L) { List<RFuture<Boolean>> futures = new ArrayList(acquiredLocks.size()); //迭代器用于遍历已经获取成功的锁 Iterator var24 = acquiredLocks.iterator(); while(var24.hasNext()) { RLock rLock = (RLock)var24.next(); //设置每一把锁的有效期 RFuture<Boolean> future = ((RedissonLock)rLock).expireAsync(unit.toMillis(leaseTime), TimeUnit.MILLISECONDS); futures.add(future); } var24 = futures.iterator(); while(var24.hasNext()) { RFuture<Boolean> rFuture = (RFuture)var24.next(); rFuture.syncUninterruptibly(); } } //但如果没设置有效期,则会触发WatchDog机制,自动帮我们设置有效期,所以大多数情况下,我们不需要自己设置有效期 return true; }

6. 适用场景

  • 多资源事务:需要同时锁定多个资源以保证事务的原子性。
  • 跨服务调用:在分布式系统中,多个服务需要协同完成某个操作时。
  • 避免死锁:通过统一的加锁和解锁机制,减少死锁风险。

7. 总结

Redisson MultiLock 通过组合多个独立的RLock实例,配合 Lua 脚本实现的原子性加锁回滚机制,为分布式系统提供了可靠的多资源锁定能力。理解其原理有助于在需要同时锁定多个资源时,设计出更健壮、更安全的并发控制方案。

http://www.cnnetsun.cn/news/3923051.html

相关文章:

  • Claude Code 安装配置全攻略:从 Node.js 环境到 Coding Plan 集成
  • 大一新生必读:大学四年高效成长指南
  • Unity时间戳转换性能优化:从DateTime到高效实现的深度解析
  • 轻量级HTTP压测工具Weighttp:原理、实战与性能分析指南
  • Unity开发中C#静态成员深度解析:从内存模型到实战避坑指南
  • YOLO乒乓球比赛落点与旋转类型目标检测数据集
  • 义乌本地生活代运营服务解析与选择指南
  • 国内AI产业格局演进:从技术竞赛到生态构建与垂直应用
  • MyBatis中resultType=“_byte[]“的用法讲解
  • 深度解析宜宾网站建设88sou如何选择:中小企业避坑指南与实战策略
  • 多体动力学仿真技术:从基础建模到高级应用
  • Spring Session与Spring Security整合Redis实现分布式会话管理
  • 揭秘泸州中泸集团建设有限公司网站背后的实力、服务与初心:为何它是您值得信赖的建筑合作伙伴
  • 儿童教育App无广告技术实现与用户体验优化
  • 新能源配电网中联合储能系统的MATLAB优化调度实践
  • FastAPI+Unicorn无依赖打包部署实战
  • AIoT技术解析:从原理到五大高价值应用场景
  • WPF+.NET6+SqlSugar全栈权限管理平台开发实践
  • 2026毕业论文AI降重工具实测合集,怎么选看这篇
  • 大兴模版网站建设哪家好?揭秘避坑指南,选对网站才是真省钱
  • 基于LLM的智能财务顾问:原理、实现与工程实践
  • Linux常用命令3
  • Windows更新组件重置工具:一键解决Windows更新故障的终极方案
  • 流处理系统版本管理的核心挑战与架构设计
  • 编写判断大小端程序
  • 为什么说ActivityThread是主线程?
  • Matlab数字滤波实战:从Butterworth到小波变换
  • 深度揭秘:天津市城乡建设网站如何成为市民办事与政策查询的核心入口
  • 芯片焊接测试实战:BGA虚焊案例的经验复盘
  • 国内AI短剧出海多语言制作服务商推荐