我的lua使用初体验

news/2025/3/3 22:28:17/文章来源:https://www.cnblogs.com/oneArbor/p/18749541

使用lua实现检查和删除分布式锁的原子性

很多时候出现并发问题的根本原因在于检查和操作不是同一个操作,不具有原子性,所以中间会被其他线程插一脚。所以我们需要有一种工具保证这两种操作的原子性

lua脚本为我们提供了这种原子性。

为什么redis执行lua脚本时具有原子性?

image

使用lua操作redis时的注意事项

image

  • 先在redis中测试操作的可行性
  • 将redis的操作转换为lua代码并添加逻辑控制
  • 在java中执行lua代码

具体实践

我们最终的操作都要集成到java代码中,所以出现了java代码 执行 lua 脚本 , lua 脚本控制redis的流程。

也就是java不直接操作redis,而是添加了一个中间件,由它代为操作redis。

但是我们编程流程则正好相反,所以我们反向编写代码。

1 redis操作
# 由我们的java代码可知,我们对redis只有以下两种操作,分别是
# 取出key所对应的线程id
# 删除对应的key
# 于是我们测试对应的命令为
get key
delete key
2 lua操作

首先要清楚当有人要执行lua脚本时,可以外部传递参数,避免硬编码的问题

image

// 将当前线程id作为参数传入
local threadId = ARGV[1]
// 将锁名称作为key值传入
local key = KEYS[1]
local lockId = redis.call("get" , key)
if(threadId == lockId) thenreturn redis.call("del" , key)
then
return 0

上述代码优化后为 :

if(ARGV[1]== redis.call("get" , KEYS[1])) thenreturn redis.call("del" , KEYS[1])
end
return 0
3 java代码中使用调用lua脚本操作redis

方法为 :

stringRedisTemplate.execute(LUA_SCRIPT , KEYS , ARGVS)

理解起来记忆就是 : 对于redis要进行何种操作呢 ?哦,就是进行lua脚本里的操作。

但是这个lua脚本我们不能写成硬编码的形式,也就是将lua代码直接插入到java代码中,会导致我们后期难以维护。

所以应该将lua代码单独写成一个文件,然后java代码去加载这个文件,然后执行这个脚本文件

private final static DefaultRedisScript<LONG> UNLOCK_SCRIPT;
static {UNLOCK_SCRIPT = new DefaultRedisScipt<LONG>();UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));UNLOCK_SCRIPT.setResultType(Long.class);
} // 写在static中可以增加代码的可读性,也方便控制逻辑// 在方法中直接调用stringRedisTemplate即可
stringRedisTemplate.execute(LUA_SCRIPT , Collections.singletonList(KEY_PREFIX + name),ID_PREFIX + Thread.currentThread().getId());
4 为什么使用Collections.singletonList()?

因为我们方法需要一个list类型的对象,所以我们要将string放入列表中返回。Collections.singletonList只是对这个过程进行了简化。

image

5.使用lua实现检查和删除分布式锁的原子性

由上可知,我们出现并发问题的根本原因在于检查和操作不是同一个操作,不具有原子性,所以中间会被其他线程插一脚。

但是lua脚本为我们提供了这种原子性。

为什么redis执行lua脚本时具有原子性?

image

使用lua操作redis时的注意事项

image

但是我们最终的操作都要集成到java代码中,所以出现了

java代码 执行 lua 脚本 , lua 脚本控制redis的流程

也就是java不直接操作redis,而是添加了一个中间件,由它代为操作redis。

但是我们编程流程则正好相反

  • 先在redis中测试操作的可行性
  • 将redis的操作转换为lua代码并添加逻辑控制
  • 在java中执行lua代码
5.1 redis操作
# 由我们的java代码可知,我们对redis只有以下两种操作,分别是
# 取出key所对应的线程id
# 删除对应的key
# 于是我们测试对应的命令为
get key
delete key
5.2 lua操作

首先要清楚当有人要执行lua脚本时,可以外部传递参数,避免硬编码的问题

(https://img2024.cnblogs.com/blog/3489813/202503/3489813-20250303222210338-616996731.png)

// 将当前线程id作为参数传入
local threadId = ARGV[1]
// 将锁名称作为key值传入
local key = KEYS[1]
local lockId = redis.call("get" , key)
if(threadId == lockId) thenreturn redis.call("del" , key)
then
return 0

上述代码优化后为 :

if(ARGV[1]== redis.call("get" , KEYS[1])) thenreturn redis.call("del" , KEYS[1])
end
return 0
5.3 java代码中使用调用lua脚本操作redis

方法为 :

stringRedisTemplate.execute(LUA_SCRIPT , KEYS , ARGVS)

理解起来记忆就是 : 对于redis要进行何种操作呢 ?哦,就是进行lua脚本里的操作。

但是这个lua脚本我们不能写成硬编码的形式,也就是将lua代码直接插入到java代码中,会导致我们后期难以维护。

所以应该将lua代码单独写成一个文件,然后java代码去加载这个文件,然后执行这个脚本文件

private final static DefaultRedisScript<LONG> UNLOCK_SCRIPT;
static {UNLOCK_SCRIPT = new DefaultRedisScipt<LONG>();UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));UNLOCK_SCRIPT.setResultType(Long.class);
} // 写在static中可以增加代码的可读性,也方便控制逻辑// 在方法中直接调用stringRedisTemplate即可
stringRedisTemplate.execute(LUA_SCRIPT , Collections.singletonList(KEY_PREFIX + name),ID_PREFIX + Thread.currentThread().getId());
5.4 为什么使用Collections.singletonList()?

因为我们方法需要一个list类型的对象,所以我们要将string放入列表中返回。Collections.singletonList只是对这个过程进行了简化。

image

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

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

相关文章

三剑客与正则系列-sed命令

1.概述核心功能:取行,过滤,替换修改文件内容 难点:后向引用(截取). sed stream editor流编辑器.2.格式命令 选项 详细格式 参数sed 选项 条件动作 文件找谁干啥找谁:条件,匹配哪一行,哪些行. 干啥:动作,增删改查. #显示文件的第3行 sed -n 3p /etc/passwd选项 说明-n 取消默认输…

加速PyTorch模型训练技巧

Pytorch-Lightning 可以在Pytorch的库Pytorch-lightning中找到我们在这里讨论的每一个优化。Lightning是在Pytorch之上的一个封装,它可以自动训练,同时让研究人员完全控制关键的模型组件。Lightning使用最新的最佳实践,并将你可能出错的地方最小化。 我们为MINST定义为Light…

能力全面提升综合题单-练习

Part1 语言基础题 P1089 [NOIP 2004 提高组] 津津的储蓄计划import java.util.Scanner;// P1089 [NOIP 2004 提高组] 津津的储蓄计划 public class P1089 {public static void main(String[] args) {Scanner in = new Scanner(System.in);int[] budget = new int[12];for (int …

kettle插件-git/svn版本管理插件

场景:大家都知道我们平时使用spoon客户端的时候时无法直接使用git的,给我们团队协作带来了一些小问题,需要我们本机单独安装git客户端进行手动上传trans或者job。 我们团队成员倪老师开发了一款kettle的git插件,帮我们解决了这个大难题,大大方便了我们团队协作,今天一起来…

自我介绍,软工五问

作业相关信息这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/SoftwareEngineeringClassof2023这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/SoftwareEngineeringClassof2023/homework/13325这个作业的目标 学习博客的使用自我介绍 我是软工2班的彭颂华…

HTTPS,TLS原理分析

简单介绍 Tansport Layer Security TLS 已经逐渐取代 SSL 可以简单理解:HTTPS = HTTP + SSL/TLS TLS运行在TCP之上,HTTP之下,传输层协议,负责HTTP内容的安全传输 TLS流程在TCP三次握手建立连接后开始 TLS协议结构wireshark中TLS主要分为两层,底层的是TLS记录协议,主要负责…

训练ai生成代码效果展示

训练ai生成代码效果展示 在本次实验中试图使用命令的形式来训练ai,达到成功获取需要的web开发页码的效果 起初 本来我打算让ai生成一个整体的项目,但是ai不可以直接生成一整块的项目,他也没办法把得到的结果以文件的形式传给你,得到的还是文本,第一实验的结果并不理想 以下是训…

公交线路(迪杰斯特拉)

这道题就是一道迪杰斯特拉的板子,没想到的是我想到了重载小于符号,没想到要定义优先队列 #include<iostream> #include<vector> #include<queue> #include<cstring> using namespace std; const int N=1000+5; struct node{int id,dis; }; vector<…

HDFS中数据目录的挂载在根目录下至磁盘爆满问题解决

1、查看hdfs-size.xml文件 获取数据目录位置vim /opt/hadoop/etc/hadoop/hdfs-site.xml<property> <name>dfs.datanode.data.dir</name> <value>/home/hadoop-data/data-node-data,/home/hadoop-data/data-node-data-2,/data-3</value> </pro…

下载及安装

进入官网https://www.docker.com/ 直接在浏览器下载会比较慢,我习惯复制下载链接后到迅雷去下载,更快一些 双击安装后会有个选项是add....什么什么,意思是添加桌面快捷方式 后有个选项是退出并重启,这个重启是重启电脑(所以截图没有了),记得保存好自己的文档 问卷调查 …

修理牛棚

思路: 有的牛棚住牛,有的是空的,从反过来的思路来想,先用一块木板盖住所有有猪住的猪棚,减去空牛棚数,最终得到最多被木板挡住门的牛棚的数量。 代码展示: #include<bits/stdc++.h> using namespace std; const int N=210; int m,s,c;int a[N],b[N];int main() {c…