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

redission实现读写锁的原理

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.xdnf.cn/news/1481275.html

相关文章:

  • TDengine 时间函数 WEEKDAY() 用户手册
  • 【PCIe EP 设备入门学习专栏 -- 8 PCIe EP 架构详细介绍】
  • dask.dataframe.shuffle.set_index中获取 divisions 的步骤分析
  • 单例模式(巨通俗易懂)普通单例,懒汉单例的实现和区别,依赖注入......
  • 【C++题解】DFS和BFS
  • leetcode 75 颜色分类
  • OS项目构建效能改进策划方案
  • 神马 M60S++ 238T矿机参数解析:高效SHA-256算法比拼
  • Docker加速下载镜像的配置指南
  • 计算机网络:物理层---数据通信基础知识
  • 【C++ 11 模板类】tuple 元组
  • 嵌入式笔记系列——UART:TTL-UART、RS-232、RS-422、RS-485
  • 旧电脑改造linux服务器2:安装系统
  • 软考中级习题与解答——第二章_程序语言与语言处理程序(3)
  • AD渗透中服务账号相关攻击手法总结(Kerberoasting、委派)
  • 数据仓库概要
  • 【selenium】网页元素找不到?从$(‘[placeholder=“手机号“]‘)说起
  • PyQt5 入门(上):开启 GUI 编程之旅
  • python advance -----object-oriented
  • URI与URL区别:资源ID和地址差异
  • Vue3中Vite的介绍与应用
  • 第1课:开篇:RAG技术与DeepSeek模型全景导读
  • Cloudflare for SaaS 实现 CNAME 接入 CDN 支持国内外智能分流建站
  • AI Agent侵入办公室
  • Android Audio Patch
  • 长尾关键词优化驱动SEO流量增长
  • 链动2+1模式:全渠道整合与用户角色化的商业逻辑与行为动机探析
  • ElasticSearch原理
  • CAN总线学习
  • HarmonyOS:通过组件导航设置信息提醒