Redisson 实现分布式读写锁的核心原理是 ​基于 Redis 的 Lua 脚本原子操作​ + ​Pub/Sub 通知机制,在保证强一致性的同时实现高效的读并发(读不阻塞读,写阻塞读)。以下是其核心设计:


一、核心数据结构

Redisson 使用 Redis 的 ​Hash 结构​ 存储锁信息:

  • Key{锁名称}(如 my_lock
  • Hash 字段:
    • mode: 锁模式(read/write
    • UUID:threadId: 持有锁的客户端标识(如 c983678b-1421-4c76-8ea0-7f3ab7d9c775:1
    • count: 锁的重入次数(支持可重入)

二、读锁(Read Lock)实现原理

1. 获取读锁流程
-- Lua 脚本原子执行
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;
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then  -- 当前线程已持有读锁redis.call('hincrby', KEYS[1], ARGV[2], 1);         -- 重入次数+1redis.call('pexpire', KEYS[1], ARGV[1]);            -- 刷新超时return nil;
end;
if (redis.call('hexists', KEYS[1], 'mode') == 1) and (redis.call('hget', KEYS[1], 'mode') == 'read') then  -- 已有其他读锁redis.call('hincrby', KEYS[1], ARGV[2], 1);         -- 直接叠加读锁计数redis.call('pexpire', KEYS[1], ARGV[1]);return nil;
end;
return redis.call('pttl', KEYS[1]);  -- 存在写锁,返回剩余时间(需等待)

关键点​:

  • 只要当前无写锁(mode 非 write),读锁可直接获取,​不阻塞其他读锁
  • 多个读锁共享同一个 Hash 结构,通过字段区分不同客户端。
2. 读锁释放
if (redis.call('hexists', KEYS[1], ARGV[2]) == 0) then return nil; end;  -- 锁不存在
local counter = redis.call('hincrby', KEYS[1], ARGV[2], -1);  -- 重入次数-1
if (counter == 0) thenredis.call('hdel', KEYS[1], ARGV[2]);  -- 移除当前线程的锁
end;
if (redis.call('hlen', KEYS[1]) == 1) then  -- 只剩 mode 字段(无任何锁)redis.call('del', KEYS[1]);              -- 删除整个 Keyredis.call('publish', KEYS[2], ARGV[1]); -- 发布解锁通知
end;
return 1;

三、写锁(Write Lock)实现原理

1. 获取写锁流程
if (redis.call('exists', KEYS[1]) == 0) then  -- 无任何锁redis.call('hset', KEYS[1], 'mode', 'write');  -- 设置为写模式redis.call('hset', KEYS[1], ARGV[2], 1);       -- 记录持有者redis.call('pexpire', KEYS[1], ARGV[1]);       -- 设置超时return nil;
end;
if (redis.call('hexists', KEYS[1], 'mode') == 1) and (redis.call('hget', KEYS[1], 'mode') == 'write') then  -- 已有写锁if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then  -- 当前线程持有写锁redis.call('hincrby', KEYS[1], ARGV[2], 1);         -- 重入次数+1redis.call('pexpire', KEYS[1], ARGV[1]);return nil;end;
end;
return redis.call('pttl', KEYS[1]);  -- 存在读锁或其他写锁,返回剩余时间(需等待)

关键点​:

  • 写锁要求绝对互斥:​必须无任何锁(读/写)存在才能获取。
  • 若存在读锁或其他写锁,客户端需等待(通过 Pub/Sub 监听解锁通知)。
2. 写锁释放
if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then return nil; end;  -- 锁不存在
local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1);  -- 重入次数-1
if (counter == 0) thenredis.call('hdel', KEYS[1], ARGV[3]);  -- 移除持有者
end;
if (redis.call('hlen', KEYS[1]) == 1) then  -- 只剩 mode 字段redis.call('del', KEYS[1]);             -- 删除 Keyredis.call('publish', KEYS[2], ARGV[1]); -- 发布解锁通知
end;
return 1;

四、阻塞等待与通知机制

1. 锁竞争时的等待策略
  • 当锁获取失败时,Redisson ​不轮询,而是通过 Redis 的 ​Pub/Sub 订阅锁释放事件​:
    // 伪代码:订阅解锁通知
    RedisPubSub listener = new RedisPubSub() {void onMessage(String channel, String message) {if (message.equals("unlock_msg")) {tryAcquireLock(); // 收到通知后重新尝试获取锁}}
    };
    redis.subscribe(listener, "lock_channel");
  • 优势​:避免频繁轮询 Redis,减少网络开销。
2. 锁超时与续期
  • 看门狗机制(Watchdog)​​:
    后台线程每隔 10 秒检查锁是否仍被持有,若持有则刷新 TTL(默认 30 秒),防止业务未完成时锁过期。
    if (lockAcquired) {scheduleExpirationRenewal(threadId); // 启动看门狗线程
    }

五、公平锁实现

Redisson 还提供公平读写锁​(按请求顺序获取锁):

  1. 使用 Redis ​List 结构作为请求队列。
  2. 每个客户端获取锁前在队列尾部追加自己的请求 ID。
  3. 只有队首的请求有权尝试获取锁,避免饥饿问题。

总结:Redisson 读写锁的核心优势

  1. 读读并发​:通过 Hash 结构叠加读锁计数,无写锁时读操作永不阻塞。
  2. 原子性​:所有锁操作通过 Lua 脚本在 Redis 单线程中执行,无竞态条件。
  3. 低开销等待​:基于 Pub/Sub 的事件通知取代轮询。
  4. 容错性​:锁超时自动释放 + 看门狗续期,避免死锁。
  5. 可重入​:支持同一线程多次加锁(通过 count 字段实现)。

​:实际代码比上述伪代码更复杂(含重试机制、异常处理等),但核心逻辑一致。建议直接阅读 Redisson 源码 中的 RedissonReadLock 和 RedissonWriteLock 类。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/921682.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/921682.shtml
英文地址,请注明出处:http://en.pswp.cn/news/921682.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【 ​​SQL注入漏洞靶场】第二关文件读写

SQLi-Labs​它是一个开源的、专门为学习 ​​Web安全​​ 和 ​​SQL注入技术​​ 而设计的靶场项目。开发者故意在代码中留下了各种不同类型的SQL注入漏洞,让安全研究人员、学生和爱好者可以在一个合法、安全的环境中进行实战练习,从而掌握发现和利用SQ…

设计艺术~缓存结构设计

背景 面对高QPS场景的业务,不得不考虑对一些数据做缓存设计,常见的缓存设计有这些:DB Proxy缓存、分布式缓存、Localcache缓存。 在考虑加缓存的背景下不考虑数据的一致性,都是瞎扯,所以我们再定义一下数据的一致性场景…

后端开发技术栈

后端开发技术栈核心技术内容平台 (Content Platform)电商 (E-Commerce)金融科技 (FinTech) / 支付物联网 (IoT - Internet of Things)游戏后端 (Game Backend)社交平台搜索平台企业级应用开发音视频处理后端地图与地理位置服务DevOps大数据开发大模型应用开发智能合约开发核心技…

【ICCV2025】计算机视觉|即插即用|ESC:颠覆Transformer!超强平替,ESC模块性能炸裂!

论文地址:https://arxiv.org/pdf/2503.06671 代码地址:https://github.com/dslisleedh/ESC 关注UP CV缝合怪,分享最计算机视觉新即插即用模块,并提供配套的论文资料与代码。 https://space.bilibili.com/473764881 摘要 本研究…

【面试场景题】如何进行高并发系统的性能测试?

文章目录一、明确测试目标与指标二、测试环境搭建三、测试工具选型四、测试场景设计五、执行测试与监控六、瓶颈分析与调优七、测试报告与迭代总结高并发系统的性能测试是验证系统在极限流量下是否能保持稳定运行的关键环节,需要结合场景设计、工具选型、指标监控、…

攻防世界ReverseMe-120

这道题比较经典,涉及三个知识点,所以记录一下。首先给了一个文件,detect it easy看了下,是32位exe。放入ida中,找下main函数,F5反编译看一下伪代码。int __cdecl main(int argc, const char **argv, const …

小白也能看懂,HTTP中的文件上传与下载到底发生了什么?

HTTP 文件传输协议解析:上传与下载 这份文档会用最简单的方式,带你了解 HTTP 协议是如何处理文件下载和上传的。我们会专注于协议本身,看看客户端(比如你的浏览器)和服务端(网站服务器)之间到底…

快速构建数据集-假数据(生成划分)

快速构建数据集-假数据1、torch.randn(✅)2、HuggingFace Datasets(✅)🔹1. 从字典生成🔹2. 从 pandas.DataFrame 生成🔹3. 批量生成“业务型”假数据(配合 Faker)&#…

[修订版]Xenomai/IPIPE源代码情景解析

[修订版]Xenomai/IPIPE源代码情景解析 第一章:Interrupt Pipeline介绍 1.1 I-pipe与Xenomai1.2 I-pipe核心概念1.3 拉取I-pipe代码 第二章:I-pipe对ARM64异常的改造 2.1 ARM64中断机制与异常处理2.2 EL0_IRQ 中断改造之入口2.3 EL0_IRQ 中断改造之中断处…

【Qt开发】按钮类控件(三)-> QCheckBox

目录 1 -> 概述 2 -> 核心特性 2.1 -> 状态管理 2.2 -> 信号机制 2.3 -> 外观与文本 3 -> 应用场景 4 -> 代码示例 5 -> 总结 1 -> 概述 QCheckBox 是 Qt 框架中提供的一个基础控件,用于实现复选框功能。它允许用户在两种或三种…

在新发布的AI论文中 pytorch 和tensorflow 的使用比例

根据 2025 年最新的学术动态和行业报告,PyTorch 在 AI 论文中的使用比例已占据绝对主导地位,而 TensorFlow 的占比持续下降。以下是基于多个权威来源的综合分析: 一、顶级会议中的框架分布 在 NeurIPS、ICML、CVPR 等顶级学术会议中&#xff…

3DXML格式是什么?用什么软件可以打开?

3DXML 是一种开放标准的数据交换格式,主要用于三维 CAD(计算机辅助设计)模型的存储和交换。它是由 Dassault Systmes 开发的一种文件格式,常用于 CATIA V6 和其他支持该格式的应用程序中。3DXML 文件可以包含完整的 3D 模型数据&a…

9月8日星期一今日早报简报微语报早读

9月8日星期一,农历七月十七,早报#微语早读。1、中国火箭与月亮同框,遥感四十号03组卫星发射成功;2、湖南郴州开发区改革:编制数由815名减至680名,精简16.6%;3、水利部对广东、广西启动洪水防御Ⅳ…

windows系统搭建MQTT服务器

1、MQTT 协议 MQTT协议:实现MQTT协议需要客户端和服务器端通讯完成。 三种身份: 发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。 消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。 MQTT&am…

从 GPT 到 LLaMA:解密 LLM 的核心架构——Decoder-Only 模型

🔥从 GPT 到 LLaMA:解密 LLM 的核心架构——Decoder-Only 模型 “为什么所有大模型(LLM)都长一个样?” 因为它们都有一个共同的“基因”——Decoder-Only 架构。 在前面两节中,我们学习了: BER…

Codeforces Round 1047 (Div. 3)

由于最近这三天的数学建模,让我这个精力本来就不多的AI手更加力竭了,没注意到昨晚的cf,所以今天来补题了。 比赛连接:比赛传送门 A题: You are doing a research paper on the famous Collatz Conjecture. In your e…

C++经典的数据结构与算法之经典算法思想:贪心算法(Greedy)

贪心算法(Greedy Algorithm):通过局部最优达成全局最优的决策策略 贪心算法是一种通过每次选择局部最优解来期望全局最优解的算法思想。它不考虑未来的影响,仅根据当前信息做出最优选择,适用于具有贪心选择性质和最优子…

LangChain实战(二十一):构建自动化AI客服系统

本文是《LangChain实战课》系列的第二十一篇,将带领您构建一个完整的自动化AI客服系统。通过结合对话记忆、工具调用和业务知识库,我们将创建一个能够处理复杂客户查询的智能客服解决方案。 前言 在现代商业环境中,客户服务是企业成功的关键因素之一。传统客服系统往往面临…

一人公司智能管理系统概述

系统概述 项目结构 Al_Compny系统采用前后端分离的全栈架构,项目根目录下包含两个主要子目录:Al_Compny_backend(后端服务)和Al_Compny_frontend(前端应用)。核心功能模块 Al_Compny系统是一个面向"一…

OpenWrt | 在 PPP 拨号模式下启用 IPv6 功能

文章目录一、WAN 口配置二、LAN 口配置三、IPv6 测试本文将详细介绍 将光猫的网络模式改成桥接之后使用路由器拨号的上网方式的情况下,在 OpenWrt 上使用 PPP 拨号模式上网时,启用 IPv6 功能的方法。 一、WAN 口配置 首先,我们需要在 网络 …