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

news2024/11/24 17:05:34

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命令,返回当前数据的过期时间

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CFA CAIA最新道德手册第14版+道德案例手册(2024年最新原创写的内容,上一版还是10年前14年写的)

纯原创CFA CAIA最新道德手册第14版道德案例手册&#xff08;2024年最新原创写的内容&#xff0c;上一版还是10年前14年写的&#xff09; standards 是CFA三个级别和CAIA两个级别重中之重&#xff0c;2014年的版本太过老旧&#xff0c;现在协会发布了新考纲&#xff0c;自己原创…

LVS(Linux virual server)

目录 一.集群和分布式简介 1.系统性能扩展方式 2.集群Cluster 3.分布式 4.集群和分布式 二.lvs(Linux virtual server) 运行原理 1.lvs介绍 2.lvs集群体系结构 3.LVS概念 4.lvs集群的类型 nat模式 nat模式数据逻辑 lvs-nat模式原理及部署方法 实验环境部署 实验流程…

Proxy302:你的一站式代理IP解决方案

一、Proxy302介绍 Proxy302&#xff0c;一款优秀的全球代理IP平台&#xff0c;以按需充值的灵活方式、覆盖广泛的代理类型及直观高效的用户上手体验与界面设计&#xff0c;赢得了市场广泛认可。Proxy302亮点不仅在于其功能的强大&#xff0c;更在于其对用户体验的深刻理解和不…

代发考生战报:7月26号北京考试通过 HCIP-Cloud云计算 H13-527

代发考生战报&#xff1a;7月26号北京考试通过 HCIP-Cloud云计算 H13-527 &#xff0c;考试遇到4个新题&#xff0c;剩下都是题库里的&#xff0c;但是没打高分&#xff0c;可能题库里的答案有问题&#xff0c;但是能考过就行&#xff0c;挺满足的&#xff0c;就是把题库都背会…

IT知识库文档查找与学习:rfc文档

RFC文档查找 RFC&#xff08;Request for Comments&#xff09;文档是互联网工程任务组&#xff08;Internet Engineering Task Force, IETF&#xff09;发布的一系列备忘录&#xff0c;旨在提供互联网技术和应用的标准、规范、指南和最佳实践。RFC文档是互联网发展的基石&…

小怡分享之String类的小练习

前言&#xff1a; &#x1f308;✨之前小怡给大家分享了String类&#xff0c;今天小怡给大家分享String类的一些小习题。 1.第一个只出现一次的字符 思路&#xff1a; 遍历字符串&#xff0c;把对应字符位置的下标开始计数&#xff0c;count[字符-‘a’]&#xff1b;再次遍历…

数模——灰色关联分析算法

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 文章目录 前言 一、基本概念了解 1.什么是灰色系统&#xff1f; 2.什么是关联分析&#xff1f; 二、模型原理 三、建模过程 1.找母序列&#xff08;参考序列&am…

力扣面试150 逆波兰表达式求值 栈 模拟栈

Problem: 150. 逆波兰表达式求值 &#x1f468;‍&#x1f3eb; 参考题解 class Solution {//纯数组模拟栈实现(推荐) 3 ms 36 MBpublic static int evalRPN(String[] tokens) {int[] numStack new int[tokens.length / 2 1];int index 0;for (String s : tokens) {swit…

常见中间件漏洞(二、WebLogin合集)

目录 二、WebLogic Weblogic介绍 2.1 后台弱口令GetShell 漏洞描述 影响范围 环境搭建 漏洞复现 2.2 CVE-2017-3506 漏洞描述 影响版本 环境搭建 漏洞复现 2.3 CVE-2019-2725 漏洞描述 影响版本 环境搭建 漏洞复现 2.4 CVE-2018-2628 漏洞描述 漏洞影响 环…

STM32学习笔记2 --- GPIO输入

目录 AD/DA 按键 传感器模块 传感器模块细节 按键模块电路 传感器模块接入电路 OLED ​编辑 代码 封装驱动代码 GPIO读取函数 按键控制LED代码 部分解释 光敏传感器代码 部分解释 接线图 c知识补充 AD/DA AD&#xff1a;模拟-数字转换 DA&#xff1a;数字-模拟…

使用Idea进行Java开发断点打不进去

我在进行打断点时 出现了这个问题导致我无法进行断点测试 问题原因&#xff1a; 打的断点太多了 &#xff0c;导致线程冲突 解决办法 &#xff1a; 删除之前打完未去掉的断点 完美解决 Nice&#xff01; 有可能会不小心点到断点失效键&#xff0c;断点有可能会变成灰色 则为无…

【系统分析师】-综合知识-企业信息化与系统规划

1、一个有效的客户关系管理&#xff08; Customer Relationship Management,CRM&#xff09;解决方案应具备畅通有效的客户交流渠道、对所获信息进行有效分析和&#xff08;CRM 与 ERP 很好地集成&#xff09;等特点。 通过将&#xff08;人力资源、业务流程与专业技术&#x…

uniapp连接手机调试App,并最终打包成apk文件,和上传到应用商店的问题

对于一些刚开始使用uniapp开发app的同学来说&#xff0c;有时候仅仅是第一步连接手机进行开发测试都很难进行。这篇文章就来向大家介绍一些怎样连接手机进行调试我们开发中的项目&#xff0c;并最终将开发好的项目打成apk包进行安装&#xff0c;和将开发好的App应用上传到应用商…

【递归】用递归的方法求阶乘

用递归的方法求 阶乘&#xff0c;这里我们以 求5&#xff01;为例&#xff0c;使用C语言实现 #include<stdio.h>int fact(int n){if(n1){return 1;}elsereturn n*fact(n-1); }int main(){int result;resultfact(5);printf("5的阶乘是 &#xff1a;%d",result)…

宝众宝达IPO终止:原实控人去世时间矛盾,婚外情主角任总经理

近日&#xff0c;上海证券交易所披露的信息显示&#xff0c;江苏宝众宝达药业股份有限公司&#xff08;下称“宝众宝达”&#xff09;及其保荐人中信建投证券撤回上市申请文件。因此&#xff0c;上海证券交易所决定终止对其首次公开发行股票并在主板上市的审核。 据贝多财经了解…

XXLJob接入说明

1、配置 1.1、pom文件 引入依赖 <dependency><groupId>com.xuxueli</groupId><artifactId>xxl-job-core</artifactId> </dependency> 1.2、bootstrap.yml 增加xxljob读取配置 common-xxljob.yaml 内容如下&#xff1a; xxl:job:adm…

JavaScript(二十七)——JavaScript 函数定义

目录 JavaScript 函数定义 函数声明 函数表达式 Function() 构造函数 函数提升&#xff08;Hoisting&#xff09; 自调用函数 函数可作为一个值使用 函数是对象 箭头函数 JavaScript 函数定义 JavaScript 使用关键字 function 定义函数。 函数可以通过声明定义&#…

力扣第45题:跳跃游戏 贪心DP(C++)

给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说&#xff0c;如果你在 nums[i] 处&#xff0c;你可以跳转到任意 nums[i j] 处: 0 < j < nums[i] i j < n 返回到达 nums[n - 1] 的最…

【附保姆级教程】两个月内快速涨粉17万,变现2w+,儿童英语阅读,家长辅导孩子必备!

目录 一、前言 二、儿童英语阅读教程 第一步&#xff1a;利用AI代写英语阅读短文 第二步&#xff1a;使用Flux生成场景图 第三步&#xff1a;将文本转为音频 第四步&#xff1a;使用canva工具进行排版 第五步&#xff1a;剪辑视频 一、前言 大家好&#xff0c;我是小奇…

如何在国外市场推广中国游戏

在国外市场推广中国游戏需要一种考虑文化差异、市场偏好和有效营销渠道的战略方法。以下是成功向国际观众介绍和推广中国游戏的关键步骤和策略&#xff1a; 进行市场调研 了解目标市场&#xff1a;首先确定哪些外国市场对你的游戏最具潜力。考虑类似游戏类型的受欢迎程度、玩…