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

redis面试(七)初识lua加锁脚本

redisson

redisson如何来进行redis分布式锁实现的源码,基于redis实现各种各样的分布式锁的原理
https://redisson.org/ 这是官网
https://github.com/redisson/redisson/wiki/Table-of-Content 这是官方文档

开始 demo

  • 建一个普通的工程
  • 在pom.xml里引入依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.8.1</version>
</dependency>  
  • 参照官网构建RedissonClient,同时看看对应的配置
    由于要在测试多节点,所以我再两台虚拟机,配置了一下redis cluster集群,标准的三主三从配置。
Config config = new Config();
config.useClusterServers()
.addNodeAddress("redis://192.168.1.1:7001")
.addNodeAddress("redis://192.168.1.1:7002")
.addNodeAddress("redis://192.168.1.1:7003")
.addNodeAddress("redis://192.168.2.2:7001")
.addNodeAddress("redis://192.168.2.2:7002")
.addNodeAddress("redis://192.168.2.2:7003");RedissonClient redisson = Redisson.create(config);
  • 简单用一下分布式锁的功能
RLock lock = redisson.getLock("anyLock");
lock.lock();
lock.unlock();RMap<String, Object> map = redisson.getMap("anyMap");
map.put("foo", "bar");  map = redisson.getMap("anyMap");
System.out.println(map.get("foo"));  

getLock()

点进getLock方法,可以看到如下

@Override
public RLock getLock(String name) {
return new RedissonLock(connectionManager.getCommandExecutor(), name);
}

getLock()方法的时候,获取到的Lock对象是RedissonLock对象,就可以了,里面封装了一个ConnectionManager里获取的一个CommandExecutor,CommandExecutor是什么东西?
既然是Connection开头的,里面一定是封装了一个跟redis之间进行通信的一个Cconnection连接对象,CommandExecutor,命令执行器,封装了一个redis连接的命令执行器,可以执行一些set、get redis的一些操作,用来执行底层的redis命令的

RedissonLock

在这个RedissonLock的构造函数里面,建议大家关注的一行代码,别的没什么,主要是一个internalLockLeaseTime的东西,跟watchdog看门狗有关系的
我们在RedissonLock里面打几个断点
在这里插入图片描述
还有一个是lock()和unlock()方法,既然调用了,肯定要看一下
在这里插入图片描述

再往后走到这里,可以看到,在默认情况下,加锁的时候,long leaseTime, TimeUnit unit,是没有的,-1和null,就代表着说,只要你加到了一把锁,就一定会永久性的持有这把锁,除非是你当前持有这把锁的机器宕机了,watchdog看门狗就会发现,然后就会释放锁,避免说永久性的一个死锁发生

再去下面142行看看tryAcquire()做了什么
在这里插入图片描述
这个里面的方法tryLockInnerAsync() 这个是核心
在这里插入图片描述

tryLockInnerAsync() 加锁lua脚本

先仔细看看这里面的一块绿色代码,明显不是java,其实这就是redis的lua脚本
在这里插入图片描述
来分析一下
if (redis.call(‘exists’, KEYS[1]) == 0) then,KEYS[1]一看就是我们设置的那个锁的名字,人家先执行了redis的exists的指令,判断一下,如果“anyLock”这个key不存在,那么就进行加锁,实际加锁的指令
"redis.call(‘hset’, KEYS[1], ARGV[2], 1); " +
"redis.call(‘pexpire’, KEYS[1], ARGV[1]); " +
"return nil; " +
hset,redis的一个指令,相当于是在redis的一个map数据结构里设置一个key value
这个map对象的名字是我们传的anyLock,然后里面的k-v键值对,k我们假设为lockState,v值呢,设置为了1
hset KEYS[1] ARGV[2] 1 转换我们传的指令后就是 hset anyLock lockState 1

pexpire KEYS[1] ARGV[1],设置一个key的过期时间
KEYS[1]其实可以理解为就是我们设置的那个key,“anyLock”,ARGV[1]其实就是一个这个key的过期时间,可能是默认的一个值,叫做30000毫秒,30s,很有可能是说的是,这个anyLock这个key对应的过期时间就是30秒

再往下的逻辑"if (redis.call(‘hexists’, KEYS[1], ARGV[2]) == 1) then " +
"redis.call(‘hincrby’, KEYS[1], ARGV[2], 1); " +
"redis.call(‘pexpire’, KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
判断名字为anyLock的这个map对象里面某个k-v键值对,key键假设为lockState,他的对应值是不是1,(上面的逻辑中可以看到),如果存在的话就把这个值用命令hincrby +1
hincrby KEYS[1] ARGV[2] 1,将anyLock这个map中的lockState这个key的值累加1
pexpire’, KEYS[1], ARGV[1]是又把这个对象设置了一下过期时间
最后的return redis.call(‘pttl’, KEYS[1]); 是返回当前还有多久就过期了

那总结一下这个加锁逻辑,我们都知道 redis中的map数据结构是键值对。
那么这个加锁的时候,就是先判断 我们定义的锁名称“anyLock” 这个map结构是否存在,如果不存在的话,就加一个默认的键值对 k-v,key=某个默认值 value=1
如果这个名为“anyLock”的map结构已经存在了,就把那个默认的键值对k-v,key=某个默认值 value+1

commandExecutor.evalWriteAsync()

OK分析完脚本,再点进这个方法来看看
在这里插入图片描述
因为现在用的是redis cluster,3主3从的模式,那么这里就有是要把key放在其中某一个master上去。
这里的第一行int slot = connectionManager.calcSlot(key);
取出来redis cluster的数量,我们都知道,redis cluster集群的节点,默认是16384个,slot的数量默认就是16384个。
那这第一行的操作就是根据key的hash值,来计算出这个key要落到应该要哪个slot节点上

下一行代码就很明显了,是根据slot节点获取到,这个slot是放在哪个master上的?
在这里插入图片描述
anyLock这个key,13434,算出来是这个slot,相当于是针对“anyLock”这个key计算出来一个hash值,然后将这个hash值对16384这个slot数量进行取模,int slot = connectionManager.calcSlot(key);
取模之后就可以拿到当前这个“anyLock”的key对应的是哪个slot
在这里插入图片描述
再回到getNodeSource()方法中看看MasterSlaveEntry
MasterSlaveEntry [masterEntry=[freeSubscribeConnectionsAmount=1, freeSubscribeConnectionsCounter=50, freeConnectionsAmount=32, freeConnectionsCounter=64, freezed=false, freezeReason=null, client=[addr=redis://192.168.1.1:7002], nodeType=MASTER, firstFail=0]]

redis://192.168.1.1:7002,编号为13434的slot所在的master是这台机器的这个端口对应的master实例
此时就是已经知道了,其实必须是将加锁的那段lua脚本,放到redis://192.168.1.1:7002这个master实例上去执行,完成加锁的操作

回过头看一下这里的代码
这里的逻辑很简单了,里面也没必要再去跟的很深了,这里大概就是上面我们分析的,拿着刚才生成的lua脚本去对应的master节点中执行脚本,把数据存入。
在这里插入图片描述
在这里插入图片描述

总结

总结一下,这里就是最基础的通过lua脚本加锁的逻辑,我们知道了redis加锁的时候是通过lua脚本将其传到redis中来加锁的。加锁的时候本质上就是新建了一个map类型的数据,key是我们的锁名称。

流程:

  • 判断key是否存在
    • 不存在的话新建一个map类型的数据,数据名称为锁名称
    • map中的数据只有一对k-v,key应该为加锁次数,默认value为1,这个主要用来做同一个线程多次加锁的重入操作
    • pexpire 命令设置过期时间,默认为30000ms 也就是30s
  • 存在的话
    • hincrby 命令,将map中的加锁次数 +1
    • pexpire 再次将过期时间设置为30000ms 也就是30s
  • pttl命令,返回当前数据的过期时间

相关文章:

redis面试(七)初识lua加锁脚本

redisson redisson如何来进行redis分布式锁实现的源码&#xff0c;基于redis实现各种各样的分布式锁的原理 https://redisson.org/ 这是官网 https://github.com/redisson/redisson/wiki/Table-of-Content 这是官方文档 开始 demo 建一个普通的工程在pom.xml里引入依赖 <…...

企元数智百年营销史的精粹:借鉴历史创造未来商机

随着时代的发展和科技的进步&#xff0c;传统营销方式正在经历前所未有的颠覆和改变。在这个数字化时代&#xff0c;企业需要不断创新&#xff0c;同时借鉴百年营销史的精粹&#xff0c;汲取历史经验&#xff0c;创造未来商机。而"企元数智"作为现代营销的代表&#…...

Java @SpringBootTest注解用法

SpringBootTest 是 Spring Framework 中的一个注解&#xff0c;用于指示 Spring Boot 应用程序的测试类。当你在测试类上使用 SpringBootTest 注解时&#xff0c;Spring Boot 会启动一个 Spring 应用程序上下文&#xff0c;并且加载应用程序的 application.properties 或 appli…...

构建智能招聘平台:人才招聘系统源码开发指南

本篇文章&#xff0c;小编将详细探讨如何基于人才招聘系统源码开发一个智能招聘平台&#xff0c;为企业的人才战略提供支持。 一、智能招聘平台的核心功能 智能招聘平台的核心在于提高招聘效率和匹配度&#xff0c;这需要集成多个关键功能模块&#xff1a; 1.职位发布与管理…...

Docker + Nacos + Spring Cloud Gateway 实现简单的动态路由配置修改和动态路由发现

1.环境准备 1.1 拉取Nacos Docker镜像 从Docker Hub拉取Nacos镜像&#xff1a; docker pull nacos/nacos-server:v2.4.01.2 生成密钥 你可以使用命令行工具生成一个不少于32位的密钥。以下是使用 OpenSSL 生成 32 字节密钥的示例&#xff1a; openssl rand -base64 321.3 …...

Linux中多线程压缩软件 | Mingz

原文链接&#xff1a;Linux中多线程压缩软件 本期教程 软件网址&#xff1a; https://github.com/hewm2008/MingZ安装&#xff1a; git clone https://github.com/hewm2008/MingZ.git cd MingZ make cd bin ./mingz -h使用源码安装&#xff1a; 若是你的git无法使用安装&am…...

【JavaEE精炼宝库】网络原理基础——UDP详解

文章目录 一、应用层二、传输层2.1 端口号&#xff1a;2.2 UDP 协议&#xff1a;2.2.1 UDP 协议端格式&#xff1a;2.2.2 UDP 存在的问题&#xff1a; 2.3 UDP 特点&#xff1a;2.4 基于 UDP 的应用层协议&#xff1a; 一、应用层 我们 Java 程序员在日常开发中&#xff0c;最…...

【回眸】周中WLB-个人

生活 计划 苏州or杭州or舟山 负负得正 烟火 鲜芋仙 办上海银行的银行卡 申请表材料准备好 个人博客提现签约变现 个人提升 yas补直播笔记&#xff08;听、口&#xff09;1~3课 *2倍 dy学堂 —— 3课时输出博客 个人笔记本搭建环境 副业探索 收集信息差 目前已…...

基于Spring boot + Vue的灾难救援系统

作者的B站地址&#xff1a;程序员云翼的个人空间-程序员云翼个人主页-哔哩哔哩视频 csdn地址&#xff1a;程序员云翼-CSDN博客 1.项目技术栈&#xff1a; 前后端分离的项目 后端&#xff1a;Springboot MybatisPlus 前端&#xff1a;Vue ElementUI 数据库&#xff1a; …...

C#进阶:轻量级ORM框架Dapper详解

C#进阶&#xff1a;轻量级ORM框架Dapper详解 在C#开发中&#xff0c;ORM&#xff08;对象关系映射&#xff09;框架是处理数据库交互的重要工具。Dapper作为一个轻量级的ORM框架&#xff0c;专为.NET平台设计&#xff0c;因其高性能和易用性而备受开发者青睐。本文将详细介绍D…...

【python015】常见成熟AI-图像识别场景算法清单(已更新)

1.欢迎点赞、关注、批评、指正&#xff0c;互三走起来&#xff0c;小手动起来&#xff01; 【python015】常见成熟AI-图像识别场景算法清单及代码【python015】常见成熟AI-图像识别场景算法清单及代码【python015】常见成熟AI-图像识别场景算法清单及代码 文章目录 1.背景介绍2…...

删除有序数组中的重复项(LeetCode)

题目 给你一个 升序排列 的数组 &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 中唯一元素的个数。 考虑 的唯一元素的数量为 &#xff0c;你需要做以下事情确…...

【算法 03】雇佣问题

“雇用问题”及其算法优化 在日常生活和工作中&#xff0c;我们经常会遇到需要从多个选项中做出选择的情况&#xff0c;而“雇用问题”正是这样一个典型的例子。在这个问题中&#xff0c;我们不仅要考虑如何高效地找到最佳候选人&#xff0c;还要关注整个过程中的成本。今天&a…...

vue3+axios请求导出excel文件

在Vue 3中使用axios请求导出Excel文件&#xff0c;可以发送一个GET或POST请求&#xff0c;并设置响应类型为blob或arraybuffer&#xff0c;然后使用new Blob()构造函数创建一个二进制文件&#xff0c;最后使用URL.createObjectURL()生成一个可以下载的链接。 先看代码 import…...

LLM与NLP

大语言模型与自然语言处理的关系&#xff1a;整体与组成的关系如 自然语言理解的编码器式&#xff08;encoder-only&#xff09;的架构是语境相关的词表示BERT&#xff1b; 自然语言转换的编码器-解码器式的&#xff08;encoder-decoder&#xff09;的架构是词频-逆文档词频T…...

js 判断是否为回文串

需求&#xff1a;忽略英文大小写和空格差异&#xff0c;判断是否为回文字符串&#xff08;例如"我爱你 你爱我"&#xff0c;"abc bA"&#xff09; 思路&#xff1a;利用翻转字符串比较&#xff0c;利用循环双指针&#xff0c;利用递归或者双循环&#xf…...

多重背包c++

题目描述 有N种物品和一个容量是V的背包。 第i种物品最多有si件&#xff0c;每件体积是vi&#xff0c;价值是wi。 求解将哪些物品装入背包&#xff0c;可使物品体积总和不超过背包容量&#xff0c;且价值总和最大。 输出最大价值。 输入 第一行两个整数&#xff0c;N&#x…...

kernel input事件测试程序

测试内核input 事件测试程序。 getevent -lt 命令查看注册的是是event0/1/2/3/4 中的哪一个。 gcc input_test.c -o input_test 编译成可执行程序。将编译的input_test&#xff0c;U盘或ADB push到系统里面&#xff0c;chmod 777 input_test 在 ./input_test input_test.c #…...

gd32 i2c 中断 主机从机双向通信例程

Master I2C0_SCL PB8 AF4 I2C0_SDA PB9 AF4 Slave I2C1_SCL PB10 AF4 I2C1_SDA PB11 AF4 //主机中断发送 void i2c_master_transmit_it(uint32_t address, uint8_t* buff, uint32_t size); //主机中断接收 void i2c_master_receive_it(uint32_t address, uint8_t* buff, uint…...

程序员在AI时代:重塑核心竞争力,共舞智能未来

程序员在AI时代&#xff1a;重塑核心竞争力&#xff0c;共舞智能未来 在这个日新月异的科技时代&#xff0c;人工智能生成内容&#xff08;AIGC&#xff09;技术&#xff0c;尤其是以ChatGPT、Midjourney、Claude等为代表的大语言模型&#xff0c;正以前所未有的速度渗透到编程…...

挑战杯推荐项目

“人工智能”创意赛 - 智能艺术创作助手&#xff1a;借助大模型技术&#xff0c;开发能根据用户输入的主题、风格等要求&#xff0c;生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用&#xff0c;帮助艺术家和创意爱好者激发创意、提高创作效率。 ​ - 个性化梦境…...

第19节 Node.js Express 框架

Express 是一个为Node.js设计的web开发框架&#xff0c;它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用&#xff0c;和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...

【位运算】消失的两个数字(hard)

消失的两个数字&#xff08;hard&#xff09; 题⽬描述&#xff1a;解法&#xff08;位运算&#xff09;&#xff1a;Java 算法代码&#xff1a;更简便代码 题⽬链接&#xff1a;⾯试题 17.19. 消失的两个数字 题⽬描述&#xff1a; 给定⼀个数组&#xff0c;包含从 1 到 N 所有…...

FastAPI 教程:从入门到实践

FastAPI 是一个现代、快速&#xff08;高性能&#xff09;的 Web 框架&#xff0c;用于构建 API&#xff0c;支持 Python 3.6。它基于标准 Python 类型提示&#xff0c;易于学习且功能强大。以下是一个完整的 FastAPI 入门教程&#xff0c;涵盖从环境搭建到创建并运行一个简单的…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练

前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1)&#xff1a;从基础到实战的深度解析-CSDN博客&#xff0c;但实际面试中&#xff0c;企业更关注候选人对复杂场景的应对能力&#xff08;如多设备并发扫描、低功耗与高发现率的平衡&#xff09;和前沿技术的…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

Linux-07 ubuntu 的 chrome 启动不了

文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了&#xff0c;报错如下四、启动不了&#xff0c;解决如下 总结 问题原因 在应用中可以看到chrome&#xff0c;但是打不开(说明&#xff1a;原来的ubuntu系统出问题了&#xff0c;这个是备用的硬盘&a…...

以光量子为例,详解量子获取方式

光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学&#xff08;silicon photonics&#xff09;的光波导&#xff08;optical waveguide&#xff09;芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中&#xff0c;光既是波又是粒子。光子本…...

【Java学习笔记】BigInteger 和 BigDecimal 类

BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点&#xff1a;传参类型必须是类对象 一、BigInteger 1. 作用&#xff1a;适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP

编辑-虚拟网络编辑器-更改设置 选择桥接模式&#xff0c;然后找到相应的网卡&#xff08;可以查看自己本机的网络连接&#xff09; windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置&#xff0c;选择刚才配置的桥接模式 静态ip设置&#xff1a; 我用的ubuntu24桌…...