Redis学习笔记13:基于spring data redis及lua脚本list列表实现环形结构案例
工作过程中需要用到环形结构,确保环上的各个节点数据唯一,如果有新的不同数据到来,则将最早入环的数据移除,每次访问环形结构都自动刷新有效期;可以基于lua 的列表list结构来实现这一功能,lua脚本可以节省网络开销、确保操作的原子性。
一个对springboot redis框架进行重写,支持lettuce、jedis、连接池、同时连接多个集群、多个redis数据库、开发自定义属性配置的开源SDK
<dependency><groupId>io.github.mingyang66</groupId><artifactId>emily-spring-boot-redis</artifactId><version>4.3.9</version>
</dependency>
GitHub地址:https://github.com/mingyang66/spring-parent
一、lua脚本实现环形结构代码
-- 判定列表中是否包含指定的value
local function contains_value(key, value)-- 获取列表指定范围内的所有元素local elements = redis.call('LRANGE', key, 0, -1)-- 泛型for迭代器for k, v in pairs(elements) doif v == value thenreturn trueendendreturn false
end-- 列表键名
local key = KEYS[1]
-- 列表值
local value = ARGV[1]
-- 列表限制长度阀值
local threshold = tonumber(ARGV[2])
-- 超时时间,单位:秒
local expire = tonumber(ARGV[3] or '0')-- pcall函数捕获多条指令执行时的异常
local success, result = pcall(function(key, value, threshold, expire)-- 获取列表长度local len = tonumber(redis.call('LLEN', key))-- 判定列表中是否包含valueif not contains_value(key, value) then-- 根据列表长度与阀值比较if len >= threshold then-- 移出并获取列表的第一个元素redis.call('LPOP', key)end-- 在列表中添加一个或多个值到列表尾部redis.call('RPUSH', key, value)end-- 超时时间必须大于0,否则永久有效if expire > 0 then-- 设置超时时间redis.call('EXPIRE', key, expire)end-- 返回列表长度return redis.call('LLEN', key)
end, key, value, threshold, expire)-- 执行成功,直接返回列表长度
if success thenreturn result
else-- 异常,则直接将异常信息返回return result
end
上述代码采用redis的pcall指令,在lua多条指令执行过程中如果有异常发生,则立马终端执行,返回异常;
二、spring data redis实现脚本执行逻辑
/*** 基于列表(List)的环* 1. 支持一直有效,threshold 设置为<=0或null* 2. 支持设置有效时长,动态刷新,interval大于0** @param redisTemplate redis 模板工具* @param key 环的键值* @param value 列表值* @param threshold 阀值,列表长度,即环上数据个数* @param expire 有效时长, 为null则永久有效* @return 当前环(列表)长度*/public static long circle(RedisTemplate redisTemplate, String key, Object value, long threshold, Duration expire) {RedisScript<Long> script = RedisScript.of(new ClassPathResource("META-INF/scripts/list_circle.lua"), Long.class);if (expire == null) {expire = Duration.ZERO;}return (Long) redisTemplate.execute(script, singletonList(key), value, threshold, expire.getSeconds());}
上述代码首先将lua脚本加载到内存中,然后将脚本进行解析,并将key及相关参数一起通过eval指令发送给redis服务器;这里遗留两个问题,一、lua脚本是如何加载到内存中的;二、每次访问同一个脚本是否需要重复读取。
三、lua脚本执行发生异常
@user_script: 44: Unknown Redis command called from Lua script
上述异常是通过redis pcall指令捕获lua脚本执行错误信息,这些错误信息会被抛出到java代码之中,可以根据这些异常信息排查脚本错误。
四、lua脚本是如何加载到内存中的?
- 首先通过如下代码创建RedisScript对象,实际是一个DefaultRedisScript对象:
RedisScript<Long> script = RedisScript.of(new ClassPathResource("META-INF/scripts/list_circle.lua"), Long.class);
- 进入RedisTemplate#execute方法,追踪发现会调用DefaultRedisScript的getSha1方法
protected <T> T eval(RedisConnection connection, RedisScript<T> script, ReturnType returnType, int numKeys,byte[][] keysAndArgs, RedisSerializer<T> resultSerializer) {...result = connection.evalSha(script.getSha1(), returnType, numKeys, keysAndArgs);...}
- DefaultRedisScript#getSha1方法实现如下
public String getSha1() {synchronized (shaModifiedMonitor) {if (sha1 == null || scriptSource.isModified()) {// 计算SHA1哈希值并转换为十六进制字符串this.sha1 = DigestUtils.sha1DigestAsHex(getScriptAsString());}return sha1;}}public String getScriptAsString() {try {//获取lua脚本字符串,通过ResourceScriptSource实现类return scriptSource.getScriptAsString();} catch (IOException e) {throw new ScriptingException("Error reading script text", e);}}
- ResourceScriptSource#getScriptAsString读取方法实现
public String getScriptAsString() throws IOException {synchronized(this.lastModifiedMonitor) {this.lastModified = this.retrieveLastModifiedTime();}Reader reader = this.resource.getReader();//从lua脚本中读取出脚本,转换为字符串返回return FileCopyUtils.copyToString(reader);}
通过上述代码可以清除的理顺lua脚本加载到内存中的整个过程,但是每次访问时都需要重复读取脚本;
五、如何实现读取一次脚本,以后直接从脚本中加载?
上述方法是通过RedisScript的of方法获取脚本对象:
static <T> RedisScript<T> of(Resource resource, Class<T> resultType) {Assert.notNull(resource, "Resource must not be null");Assert.notNull(resultType, "ResultType must not be null");DefaultRedisScript<T> script = new DefaultRedisScript<>();script.setResultType(resultType);script.setLocation(resource);return script;}
RedisScript类其实还有另外一个接受lua脚本字符串的of方法,如下:
static <T> RedisScript<T> of(String script, Class<T> resultType) {Assert.notNull(script, "Script must not be null");Assert.notNull(resultType, "ResultType must not be null");return new DefaultRedisScript<>(script, resultType);}
可以将脚本读取出来之后存到静态变量中,以后每次直接从变量中获取就可以了:
/*** 基于lua列表的环形结构实现脚本*/public static String LUA_SCRIPT_CIRCLE;public static long circle(RedisTemplate redisTemplate, String key, Object value, long threshold, Duration expire) {try {if (StringUtils.isEmpty(LUA_SCRIPT_CIRCLE)) {LUA_SCRIPT_CIRCLE = getLuaScript("META-INF/scripts/list_circle.lua");}RedisScript<Long> script = RedisScript.of(LUA_SCRIPT_CIRCLE, Long.class);}
相关文章:
Redis学习笔记13:基于spring data redis及lua脚本list列表实现环形结构案例
工作过程中需要用到环形结构,确保环上的各个节点数据唯一,如果有新的不同数据到来,则将最早入环的数据移除,每次访问环形结构都自动刷新有效期;可以基于lua 的列表list结构来实现这一功能,lua脚本可以节省网…...
c# 将excel导入 sqlite
nuget 须要加载 EPPlus.Core ExcelDataReader ExcelDataReader.DataSet //需要引用的扩展 using ExcelDataReader; using ExcelPackage OfficeOpenXml.ExcelPackage; public static void CreateZhouPianChaTable(){string tbname "zhou_pian_cha1";//判断表是否存…...
KafkaConsumer 消费逻辑
版本:kafka-clients-2.0.1.jar 之前想写个插件修改 kafkaConsumer 消费者的逻辑,根据 header 过滤一些消息。于是需要了解一下 kafkaConsumer 具体是如何拉取消费消息的,确认在消费之前过滤掉消息是否会有影响。 下面是相关的源码࿰…...
scss 实用教程
变量 $ 定义变量 $link-color: blue;变量名可以与css中的属性名和选择器名称相同 使用变量 a {color: $link_color; }$highlight-border: 1px solid $link_color;中划线和下划线相互兼容,即中划线声明的变量可以使用下划线的方式引用,反之亦然。 $li…...
NO.304 二维区域和检索 - 矩阵不可变
题目 给定一个二维矩阵 matrix,以下类型的多个请求: 计算其子矩形范围内元素的总和,该子矩阵的 左上角 为 (row1, col1) ,右下角 为 (row2, col2) 。 实现 NumMatrix 类: NumMatrix(int[][] matrix) 给定整数矩阵 …...
牛客---简单密码python
现在有一种密码变换算法。 九键手机键盘上的数字与字母的对应: 1--1, abc--2, def--3, ghi--4, jkl--5, mno--6, pqrs--7, tuv--8 wxyz--9, 0--0,把密码中出现的小写字母都变成九键键盘对应的数字,如:a 变成 2&#x…...
devops完整搭建教程(gitlab、jenkins、harbor、docker)
devops完整搭建教程(gitlab、jenkins、harbor、docker) 文章目录 devops完整搭建教程(gitlab、jenkins、harbor、docker)1.简介:2.工作流程:3.优缺点4.环境说明5.部署前准备工作5.1.所有主机永久关闭防火墙…...
页面上时间显示为数字 后端返回给前端 response java系统
有时候,在一个系统里,会看到,有的页面时间显示正常,有的页面时间显示成数字。像这样: "createTime": 1698706491000 这是因为出参没有做转换,直接将java.util.Date类型的数据返回给前端了。 返…...
idea怎么配置tomcat
要在IntelliJ IDEA中配置Tomcat,请按照以下步骤操作: 打开IntelliJ IDEA,点击File -> Settings(或者使用快捷键CtrlAltS)。 在设置窗口左侧导航栏中,选择Build, Execution, Deployment -> Applicati…...
GoLong的学习之路(二十三)进阶,语法之并发(go最重要的特点)(锁,sync包,原子操作)
这章是我并发系列中最后的一章。这章主要讲的是锁。但是也会讲上一章channl遗留下的一些没有讲到的内容。select关键字的用法,以及错误的一些channl用法。废话不多说。。。 文章目录 select多路复用通道错误示例并发安全和锁问题描述互斥锁读写互斥锁 syncsync.Wait…...
asp.net core 生命周期
在ASP.NET Core中,有三个重要的生命周期阶段: 请求生命周期(Request Lifecycle):请求生命周期从接收到客户端的HTTP请求开始,到响应结果发送给客户端结束。在请求生命周期中,ASP.NET Core会创建…...
Leetcode刷题详解—— 目标和
1. 题目链接:494. 目标和 2. 题目描述: 给你一个非负整数数组 nums 和一个整数 target 。 向数组中的每个整数前添加 或 - ,然后串联起所有整数,可以构造一个 表达式 : 例如,nums [2, 1] ,可…...
学习c#的第六天
目录 C# 判断 if 语句 嵌套 if 语句 switch 语句 嵌套 switch 语句 ? : 运算符 C# 循环 循环类型 while 循环 for/foreach 循环 do...while 循环 嵌套循环 循环控制语句 break 语句 continue 语句 无限循环 C# 判断 if 语句 在C#中,if语句用于根…...
第七章 :Spring Boot web开发常用注解(二)
第七章 :Spring Boot web开发常用注解(二) 前言 本章节知识重点:作者结合自身开发经验,以及觉察到的一个现象:Springboot注解全面理解和掌握的并不多,对注解进行了全面总结,共分两个章节,可以作为web开发工程师注解参考手册,SpringBoot常用注解大全,一目了然!。本…...
IOC - Google Guice
Google Guice是一个轻量级的依赖注入框架,专注于依赖注入和IoC,适用于中小型应用。 Spring Framework是一个全面的企业级框架,提供了广泛的功能,适用于大型企业应用。 是吧!IOC 容器不止Spring,还有Google Guice,来体…...
国际阿里云:Linux实例负载高问题排查和异常处理!!!
问题描述 在您使用ECS实例过程中,可能会遇到实例系统负载较高的情况,负载过高,可能会引发一系列异常问题,简单说您如下: CPU使用率或负载过高:一般来说,当CPU使用率≥80%时,定义为C…...
【数据结构】二叉树的遍历递归算法详解
二叉树的遍历 💫二叉树的结点结构定义💫创建一个二叉树结点💫在主函数中手动创建一颗二叉树💫二叉树的前序遍历💫调用栈递归——实现前序遍历💫递归实现中序和后序遍历 💫二叉树的结点结构定义 …...
百度王颖:百度文库以AI创作能力突破语言边界,促进思想碰撞和文化融通
1月9日,2023年世界互联网大会乌镇峰会“网络传播与文明交流互鉴论坛”召开。百度副总裁、互娱和垂类平台负责人王颖出席并发表“以技术搭建跨文化交流桥梁”主题演讲。她表示,在大模型的加持下,百度各个产品都在重构,通过技术助力…...
人工智能基础_机器学习023_理解套索回归_认识L1正则---人工智能工作笔记0063
然后上一节我们说了L1,L2正则是为了提高,模型的泛化能力, 提高泛化能力,实际上就是把模型的公式的w,权重值,变小对吧. 然后我们这里首先看第一个L1正则,是怎么做到把w权重变小的 可以看到最上面是线性回归的损失函数,然后 L1可以看到,这个正则,就是在损失函数的基础上给损失…...
Learning an Animatable Detailed 3D Face Model from In-The-Wild Images论文笔记
Learning an Animatable Detailed 3D Face Model from In-The-Wild Images论文笔记 论文目标:提出一个端到端的框架,可以从非受控的图片中学习高质量、可动画的3D人脸模型。论文方法:论文结果:论文意义: 论文目标:提出一个端到端的框架,可以从非受控的图片中学习高质量、可动画…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...
HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...
RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill
视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...
MySQL体系架构解析(三):MySQL目录与启动配置全解析
MySQL中的目录和文件 bin目录 在 MySQL 的安装目录下有一个特别重要的 bin 目录,这个目录下存放着许多可执行文件。与其他系统的可执行文件类似,这些可执行文件都是与服务器和客户端程序相关的。 启动MySQL服务器程序 在 UNIX 系统中,用…...
多模态大语言模型arxiv论文略读(110)
CoVLA: Comprehensive Vision-Language-Action Dataset for Autonomous Driving ➡️ 论文标题:CoVLA: Comprehensive Vision-Language-Action Dataset for Autonomous Driving ➡️ 论文作者:Hidehisa Arai, Keita Miwa, Kento Sasaki, Yu Yamaguchi, …...
基于机器学习的智能故障预测系统:构建与优化
前言 在现代工业生产中,设备故障不仅会导致生产中断,还会带来巨大的经济损失。传统的故障检测方法依赖于人工巡检和定期维护,这种方式效率低下且难以提前预测潜在故障。随着工业物联网(IIoT)和机器学习技术的发展&…...
阿里云Alibaba Cloud安装Docker与Docker compose【图文教程】
个人记录 进入控制台,找到定时与自动化任务 进入‘安装/卸载扩展程序’ 点击‘安装扩展程序’ 选择docker社区版,点击下一步与确定,等待一会 安装成功 查询版本 查询docker sudo docker version查询docker compose sudo docker compo…...
2025-05-01-决策树算法及应用
决策树算法及应用 参考资料 GitHub - zhaoyichanghong/machine_learing_algo_python: implement the machine learning algorithms by p(机器学习相关的 github 仓库)决策树实现与应用决策树 概述 机器学习算法分类 决策树算法 决策树是一种以树状结构对数据进行划分的分类…...
LabVIEW的MathScript Node 绘图功能
该VI 借助 LabVIEW 的 MathScript Node,结合事件监听机制,实现基于 MathScript 的绘图功能,并支持通过交互控件自定义绘图属性。利用 MathScript 编写脚本完成图形初始化,再通过LabVIEW 事件结构响应用户操作,动态修改…...
