Redisson 分布式锁源码 01:可重入锁加锁
ztj100 2024-11-04 15:16 59 浏览 0 评论
前言
相信小伙伴都是使用分布式服务,那一定绕不开分布式服务中数据并发更新问题!
单系统很容易想到 Java 的各种锁,像 synchronize、ReentrantLock 等等等,那分布式系统如何处理?
当然是使用分布式锁。
如果小伙伴不知道什么是分布式锁,那推荐看看石杉老师的突击课或者在网上搜一搜相关资料。
当使用 Redis 作为分布式锁时,当前使用较多的框架就是 Redisson。
当然 Redisson 也不仅仅只能当做锁来使用,也有很多其他的功能,小伙伴们可以看一看官方文档,自己多动手实践一下。
下面就开始记录 Redisson 的相关笔记!错误之处,欢迎指正。
环境配置
- 本地环境搭建的伪集群:
- redisson 3.15.6
不同版本可能会有所不同,但是核心思想不会发生太大变化,如果变化很大,希望可以留言。
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.15.6</version>
</dependency>
- 项目准备
一个简单的 maven 项目,只需要一个 Main 方法即可。
可重入锁加锁
在 lock.lock() 断点,作为源码入口。
默认加锁,什么参数也没有传递。但是这里会设置 leaseTime = -1。这个 leaseTime 的含义是加锁的时间。
剩下的一路挺进即可。
在调用 tryAcquire 方法之前,多了一个参数 threadId,是当前线程的 id,long 型正数。
异步加锁
直接来到 tryAcquireAsync 异步加锁方法。
tryAcquireAsync
前面已经说了 leaseTime 是 -1,所以这里会走到下面的方法中。
至此几个参数已经清楚:
- waitTime:-1;
- internalLockLeaseTime:使用默认时间 30000 毫秒;
- TimeUnit.MILLISECONDS:单位毫秒;
- threadId:线程 id;
- RedisCommands.EVAL_LONG:eval。
Redis eval 命令的相关文档可以阅读:https://redis.io/commands/eval
加锁逻辑
真正的加锁,其实就是这么一段 lua 脚本。
先说明一下 lua 脚本的参数信息:
- KEYS[1]:getRawName(),加锁的 key ,比如 anyLock;
- ARGV[1]:unit.toMillis(leaseTime),锁的毫秒时间,比如 30000;
- ARGV[2]:getLockName(threadId),是 UUID 和线程 id 拼接起来的字符串,比如 931573de-903e-42fd-baa7-428ebb7eda80:1。
因为使用的是 lua 脚本,可以保证这一块 lua 脚本的原子性。
首次加锁分析:
- exists 命令判断 redis anyLock 是否存在;
- 不存在,使用 hincrby 命令,创建 anyLock 数据;
- 对 anyLock 设置过期时间。
加锁后 Redis 内的数据格式是:
关于 Redis 的 Hash 数据结构可以阅读:https://redis.io/topics/data-types#hashes
抽象一点可以理解为 anyLock 下面挂着一个 K-V 结构的数据:
"anyLock":{
"f400aad5-4b1f-4246-a81e-80c2717c3afb:1":"1"
}
执行脚本
后续的内容就是进行请求执行 lua 脚本,唯一需要注意的地方就是有个哈希槽路由。
这块代码是在 CommandAsyncService#evalWriteAsync 方法处调用的,是为了获取一个 NodeSource。
当然这个 NodeSource 里面只存放了一个 slot(哈希槽值)。
这个 slot 值是对加锁的 key 使用 CRC16 算法计算出来的。
// MAX_SLOT 默认 16384
int result = CRC16.crc16(key.getBytes()) % MAX_SLOT;
这块计算一个 slot 到底有什么用呢?
继续追踪!
BaseRedisBatchExecutor#addBatchCommandData 在这里会从 source 里面获取到 solt,然后获得 MasterSlaveEntry。
大概可以理解为这里是获取到这个 Redis key 对应的节点。
可重入
既然是可重入锁,这块是支持可重入的,来看下可重入是如何保证的。
- exists 命令判断 redis key field 是否存在;
- 存在 则通过 hincrby 命令对 key 的 field 对应 value 自增;
- 为当前 redis key 设置过期时间。
加锁互斥
上面已经验证了两种情况:
- redis key 不存在;
- redis key 和 key 的 field 存在。
剩下的情况就是 key 存在的情况下,但是 field 不存在。
要知道 key 的 field 放的是 UUID:ThreadId,说明加锁的不是当前线程。这时候直接返回当前锁的剩余时间。
总结
本文主要介绍了 Redisson 可重入锁的加锁、锁重入、锁互斥逻辑。
核心重点在 lua 脚本。 同时需要理解 Redis 的 Hash 数据结构。
同时需要记住,在未指定加锁时间时,默认使用的是 30s。
最后,一张图介绍本文加锁逻辑。
相关推荐
- sharding-jdbc实现`分库分表`与`读写分离`
-
一、前言本文将基于以下环境整合...
- 三分钟了解mysql中主键、外键、非空、唯一、默认约束是什么
-
在数据库中,数据表是数据库中最重要、最基本的操作对象,是数据存储的基本单位。数据表被定义为列的集合,数据在表中是按照行和列的格式来存储的。每一行代表一条唯一的记录,每一列代表记录中的一个域。...
- MySQL8行级锁_mysql如何加行级锁
-
MySQL8行级锁版本:8.0.34基本概念...
- mysql使用小技巧_mysql使用入门
-
1、MySQL中有许多很实用的函数,好好利用它们可以省去很多时间:group_concat()将取到的值用逗号连接,可以这么用:selectgroup_concat(distinctid)fr...
- MySQL/MariaDB中如何支持全部的Unicode?
-
永远不要在MySQL中使用utf8,并且始终使用utf8mb4。utf8mb4介绍MySQL/MariaDB中,utf8字符集并不是对Unicode的真正实现,即不是真正的UTF-8编码,因...
- 聊聊 MySQL Server 可执行注释,你懂了吗?
-
前言MySQLServer当前支持如下3种注释风格:...
- MySQL系列-源码编译安装(v5.7.34)
-
一、系统环境要求...
- MySQL的锁就锁住我啦!与腾讯大佬的技术交谈,是我小看它了
-
对酒当歌,人生几何!朝朝暮暮,唯有己脱。苦苦寻觅找工作之间,殊不知今日之事乃我心之痛,难道是我不配拥有工作嘛。自面试后他所谓的等待都过去一段时日,可惜在下京东上的小金库都要见低啦。每每想到不由心中一...
- MySQL字符问题_mysql中字符串的位置
-
中文写入乱码问题:我输入的中文编码是urf8的,建的库是urf8的,但是插入mysql总是乱码,一堆"???????????????????????"我用的是ibatis,终于找到原因了,我是这么解决...
- 深圳尚学堂:mysql基本sql语句大全(三)
-
数据开发-经典1.按姓氏笔画排序:Select*FromTableNameOrderByCustomerNameCollateChinese_PRC_Stroke_ci_as//从少...
- MySQL进行行级锁的?一会next-key锁,一会间隙锁,一会记录锁?
-
大家好,是不是很多人都对MySQL加行级锁的规则搞的迷迷糊糊,一会是next-key锁,一会是间隙锁,一会又是记录锁。坦白说,确实还挺复杂的,但是好在我找点了点规律,也知道如何如何用命令分析加...
- 一文讲清怎么利用Python Django实现Excel数据表的导入导出功能
-
摘要:Python作为一门简单易学且功能强大的编程语言,广受程序员、数据分析师和AI工程师的青睐。本文系统讲解了如何使用Python的Django框架结合openpyxl库实现Excel...
- 用DataX实现两个MySQL实例间的数据同步
-
DataXDataX使用Java实现。如果可以实现数据库实例之间准实时的...
- MySQL数据库知识_mysql数据库基础知识
-
MySQL是一种关系型数据库管理系统;那废话不多说,直接上自己以前学习整理文档:查看数据库命令:(1).查看存储过程状态:showprocedurestatus;(2).显示系统变量:show...
- 如何为MySQL中的JSON字段设置索引
-
背景MySQL在2015年中发布的5.7.8版本中首次引入了JSON数据类型。自此,它成了一种逃离严格列定义的方式,可以存储各种形状和大小的JSON文档,例如审计日志、配置信息、第三方数据包、用户自定...
你 发表评论:
欢迎- 一周热门
-
-
MySQL中这14个小玩意,让人眼前一亮!
-
旗舰机新标杆 OPPO Find X2系列正式发布 售价5499元起
-
【VueTorrent】一款吊炸天的qBittorrent主题,人人都可用
-
面试官:使用int类型做加减操作,是线程安全吗
-
C++编程知识:ToString()字符串转换你用正确了吗?
-
【Spring Boot】WebSocket 的 6 种集成方式
-
PyTorch 深度学习实战(26):多目标强化学习Multi-Objective RL
-
pytorch中的 scatter_()函数使用和详解
-
与 Java 17 相比,Java 21 究竟有多快?
-
基于TensorRT_LLM的大模型推理加速与OpenAI兼容服务优化
-
- 最近发表
- 标签列表
-
- idea eval reset (50)
- vue dispatch (70)
- update canceled (42)
- order by asc (53)
- spring gateway (67)
- 简单代码编程 贪吃蛇 (40)
- transforms.resize (33)
- redisson trylock (35)
- 卸载node (35)
- np.reshape (33)
- torch.arange (34)
- npm 源 (35)
- vue3 deep (35)
- win10 ssh (35)
- vue foreach (34)
- idea设置编码为utf8 (35)
- vue 数组添加元素 (34)
- std find (34)
- tablefield注解用途 (35)
- python str转json (34)
- java websocket客户端 (34)
- tensor.view (34)
- java jackson (34)
- vmware17pro最新密钥 (34)
- mysql单表最大数据量 (35)