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

Redisson 分布式锁核心机制解析

Redisson 分布式锁(RLock)的可靠性依赖于其三大核心机制:用于保证原子性的 Lua 脚本、用于自动续期的看门狗(Watchdog)以及用于高效等待的发布/订阅(Pub/Sub)模型。本文旨在解析这三个机制的内部工作原理。

一、Lua 脚本与原子性保证

分布式锁的加锁操作包含多个步骤(如 existshsetpexpire),必须作为一个原子单元执行。Redisson 通过将这些操作封装到 Lua 脚本中,利用 Redis 服务端原子性执行脚本的特性,来消除并发竞态条件。

核心 tryLock Lua 脚本逻辑:

-- KEYS[1]: 锁名
-- ARGV[1]: 锁过期时间
-- ARGV[2]: 锁持有者标识 (uuid:threadId)if (redis.call('exists', KEYS[1]) == 0) thenredis.call('hset', KEYS[1], ARGV[2], 1);redis.call('pexpire', KEYS[1], ARGV[1]);return nil;
end;
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) thenredis.call('hincrby', KEYS[1], ARGV[2], 1);redis.call('pexpire', KEYS[1], ARGV[1]);return nil;
end;
return redis.call('pttl', KEYS[1]);

二、看门狗(Watchdog)的锁续期机制

为防止因业务执行时间过长导致锁提前过期,Redisson 引入了看门狗机制。

该机制在调用不带 leaseTime 参数的 lock() 方法时被激活。lock() 内部会为 leaseTime 传入 -1,这便是触发看门狗的信号。加锁成功后,若 leaseTime-1,Redisson 会启动一个后台定时任务,负责锁的自动续期。

源码逻辑:

// RedissonLock.java
private <T> RFuture<T> tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {if (leaseTime != -1) { // leaseTime 为 -1 是激活看门狗的关键return tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG);}RFuture<Long> ttlFuture = tryLockInnerAsync(waitTime, internalLockLeaseTime, // 使用默认 30sTimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);ttlFuture.onComplete((ttl, e) -> {if (e == null) { // 加锁成功后,调度续期任务scheduleExpirationRenewal(threadId);}});return (RFuture<T>) ttlFuture;
}private void scheduleExpirationRenewal(long threadId) {newTimeout(new TimerTask() {@Overridepublic void run(Timeout timeout) throws Exception {RFuture<Boolean> future = renewExpirationAsync(threadId);future.onComplete((res, e) -> {if (res) { // 续期成功,则在 10s (默认时间的1/3) 后再次执行scheduleExpirationRenewal(threadId);}});}}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
}

默认情况下,锁的过期时间为 30 秒,看门狗会每隔 10 秒检查一次,如果锁仍被当前线程持有,就将其过期时间重置为 30 秒。unlock() 操作会取消该续期任务。

三、发布/订阅(Pub/Sub)的高效等待模型

为避免获取锁失败的线程进行无效的循环轮询(busy-polling),Redisson 采用 Redis 的发布/订阅模型实现高效等待。

当线程尝试获取锁失败时,它会订阅一个与锁名相关的特定 Channel,然后在一个客户端的 Semaphore 上阻塞等待。当锁被释放时(unlock),会向该 Channel 发布一条消息。订阅者收到消息后,释放 Semaphore,从而唤醒等待的线程,使其重新尝试获取锁。

源码逻辑:

// LockPubSub.java - 订阅端
private final Semaphore latch = new Semaphore(0);
private final ChannelFuture subscribeFuture;// 当收到消息时的回调
@Override
protected void onMessage(String channel, Object msg) {if (msg.equals(UNLOCK_MESSAGE)) {latch.release(); // 释放信号量,唤醒等待线程}
}
public void await() {latch.acquire(); // 在信号量上阻塞等待
}// RedissonLock.java - 等待逻辑
private boolean tryLock(long waitTime, ... ) {// ... 尝试加锁失败后 ...// 1. 订阅解锁 channelCompletableFuture<RedissonLockEntry> subscribeFuture = subscribe(getChannelName());// 2. 在 latch 上等待,waitTime 为超时上限boolean acquired = entry.getLatch().tryAcquire(waitTime, unit);// 3. 取消订阅unsubscribe(subscribeFuture);// 4. 被唤醒后,再次尝试获取锁if (acquired) {return tryAcquireOnce(...);}return false;
}

这种事件驱动的模式将等待过程中的资源消耗降至最低,避免了对 Redis 服务器产生不必要的轮询压力。

http://www.dtcms.com/a/338933.html

相关文章:

  • Flink原理与实践:第一章大数据技术概述总结
  • 微软行业案例:英格兰足球超级联赛(Premier League)
  • 丝杆支撑座在自动化生产线中的关键支撑
  • arcgis-提取范围中最大占比面积的信息或唯一值
  • Jenkins服务器SSH公钥配置步骤
  • nodejs koa框架使用
  • 计算机大数据毕业设计推荐:基于Spark的气候疾病传播可视化分析系统【Hadoop、python、spark】
  • 《算法导论》第 30 章:多项式与快速傅里叶变换(FFT)
  • vue3中封装hooks
  • uniapp 应用未安装:软件包与现有软件包存在冲突
  • GEO 优化专家孟庆涛:技术破壁者重构 AI 时代搜索逻辑
  • Flask 路由与视图函数绑定机制
  • 正式签约 | OpenLoong 项目正式捐赠至开放原子开源基金会,成为全国首个具身智能方向孵化项目!
  • 【图像算法 - 18】慧眼辨良莠:基于深度学习与OpenCV的麦田杂草智能识别检测系统(附完整代码)
  • 哈希:两数之和
  • Mac(七)右键新建文件的救世主 iRightMouse
  • python将epub文件转pdf
  • UniApp 实现pdf上传和预览
  • 大模型级部署:从本地轻量化到云原生方案
  • 基于单片机智能密码锁/密码箱/门锁/门禁系统
  • Python爬虫实战:研究ICP-Checker,构建ICP 备案信息自动查询系统
  • PiscCode迅速集成YOLO-Pose 实现姿态关键点轨迹跟踪应用
  • 从繁琐到优雅:Java Lambda 表达式全解析与实战指南
  • 第1章 React组件开发基础
  • JxBrowser 8.10.0 版本发布啦!
  • iOS App 混淆工具实战,教育培训类 App 的安全保护方案
  • CTFshow系列——命令执行web34-37
  • RK3128增加usb调试模式,开放adb和root权限
  • leetcode 有效的括号
  • 西游记23:三藏不忘本,四圣试禅心;八戒挑行李;分工明确;