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

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列表实现环形结构案例

工作过程中需要用到环形结构&#xff0c;确保环上的各个节点数据唯一&#xff0c;如果有新的不同数据到来&#xff0c;则将最早入环的数据移除&#xff0c;每次访问环形结构都自动刷新有效期&#xff1b;可以基于lua 的列表list结构来实现这一功能&#xff0c;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 消费逻辑

版本&#xff1a;kafka-clients-2.0.1.jar 之前想写个插件修改 kafkaConsumer 消费者的逻辑&#xff0c;根据 header 过滤一些消息。于是需要了解一下 kafkaConsumer 具体是如何拉取消费消息的&#xff0c;确认在消费之前过滤掉消息是否会有影响。 下面是相关的源码&#xff0…...

scss 实用教程

变量 $ 定义变量 $link-color: blue;变量名可以与css中的属性名和选择器名称相同 使用变量 a {color: $link_color; }$highlight-border: 1px solid $link_color;中划线和下划线相互兼容&#xff0c;即中划线声明的变量可以使用下划线的方式引用&#xff0c;反之亦然。 $li…...

NO.304 二维区域和检索 - 矩阵不可变

题目 给定一个二维矩阵 matrix&#xff0c;以下类型的多个请求&#xff1a; 计算其子矩形范围内元素的总和&#xff0c;该子矩阵的 左上角 为 (row1, col1) &#xff0c;右下角 为 (row2, col2) 。 实现 NumMatrix 类&#xff1a; NumMatrix(int[][] matrix) 给定整数矩阵 …...

牛客---简单密码python

现在有一种密码变换算法。 九键手机键盘上的数字与字母的对应&#xff1a; 1--1&#xff0c; abc--2, def--3, ghi--4, jkl--5, mno--6, pqrs--7, tuv--8 wxyz--9, 0--0&#xff0c;把密码中出现的小写字母都变成九键键盘对应的数字&#xff0c;如&#xff1a;a 变成 2&#x…...

devops完整搭建教程(gitlab、jenkins、harbor、docker)

devops完整搭建教程&#xff08;gitlab、jenkins、harbor、docker&#xff09; 文章目录 devops完整搭建教程&#xff08;gitlab、jenkins、harbor、docker&#xff09;1.简介&#xff1a;2.工作流程&#xff1a;3.优缺点4.环境说明5.部署前准备工作5.1.所有主机永久关闭防火墙…...

页面上时间显示为数字 后端返回给前端 response java系统

有时候&#xff0c;在一个系统里&#xff0c;会看到&#xff0c;有的页面时间显示正常&#xff0c;有的页面时间显示成数字。像这样&#xff1a; "createTime": 1698706491000 这是因为出参没有做转换&#xff0c;直接将java.util.Date类型的数据返回给前端了。 返…...

idea怎么配置tomcat

要在IntelliJ IDEA中配置Tomcat&#xff0c;请按照以下步骤操作&#xff1a; 打开IntelliJ IDEA&#xff0c;点击File -> Settings&#xff08;或者使用快捷键CtrlAltS&#xff09;。 在设置窗口左侧导航栏中&#xff0c;选择Build, Execution, Deployment -> Applicati…...

GoLong的学习之路(二十三)进阶,语法之并发(go最重要的特点)(锁,sync包,原子操作)

这章是我并发系列中最后的一章。这章主要讲的是锁。但是也会讲上一章channl遗留下的一些没有讲到的内容。select关键字的用法&#xff0c;以及错误的一些channl用法。废话不多说。。。 文章目录 select多路复用通道错误示例并发安全和锁问题描述互斥锁读写互斥锁 syncsync.Wait…...

asp.net core 生命周期

在ASP.NET Core中&#xff0c;有三个重要的生命周期阶段&#xff1a; 请求生命周期&#xff08;Request Lifecycle&#xff09;&#xff1a;请求生命周期从接收到客户端的HTTP请求开始&#xff0c;到响应结果发送给客户端结束。在请求生命周期中&#xff0c;ASP.NET Core会创建…...

Leetcode刷题详解—— 目标和

1. 题目链接&#xff1a;494. 目标和 2. 题目描述&#xff1a; 给你一个非负整数数组 nums 和一个整数 target 。 向数组中的每个整数前添加 或 - &#xff0c;然后串联起所有整数&#xff0c;可以构造一个 表达式 &#xff1a; 例如&#xff0c;nums [2, 1] &#xff0c;可…...

学习c#的第六天

目录 C# 判断 if 语句 嵌套 if 语句 switch 语句 嵌套 switch 语句 ? : 运算符 C# 循环 循环类型 while 循环 for/foreach 循环 do...while 循环 嵌套循环 循环控制语句 break 语句 continue 语句 无限循环 C# 判断 if 语句 在C#中&#xff0c;if语句用于根…...

第七章 :Spring Boot web开发常用注解(二)

第七章 :Spring Boot web开发常用注解(二) 前言 本章节知识重点:作者结合自身开发经验,以及觉察到的一个现象:Springboot注解全面理解和掌握的并不多,对注解进行了全面总结,共分两个章节,可以作为web开发工程师注解参考手册,SpringBoot常用注解大全,一目了然!。本…...

IOC - Google Guice

Google Guice是一个轻量级的依赖注入框架&#xff0c;专注于依赖注入和IoC&#xff0c;适用于中小型应用。 Spring Framework是一个全面的企业级框架&#xff0c;提供了广泛的功能&#xff0c;适用于大型企业应用。 是吧&#xff01;IOC 容器不止Spring,还有Google Guice,来体…...

国际阿里云:Linux实例负载高问题排查和异常处理!!!

问题描述 在您使用ECS实例过程中&#xff0c;可能会遇到实例系统负载较高的情况&#xff0c;负载过高&#xff0c;可能会引发一系列异常问题&#xff0c;简单说您如下&#xff1a; CPU使用率或负载过高&#xff1a;一般来说&#xff0c;当CPU使用率≥80%时&#xff0c;定义为C…...

【数据结构】二叉树的遍历递归算法详解

二叉树的遍历 &#x1f4ab;二叉树的结点结构定义&#x1f4ab;创建一个二叉树结点&#x1f4ab;在主函数中手动创建一颗二叉树&#x1f4ab;二叉树的前序遍历&#x1f4ab;调用栈递归——实现前序遍历&#x1f4ab;递归实现中序和后序遍历 &#x1f4ab;二叉树的结点结构定义 …...

百度王颖:百度文库以AI创作能力突破语言边界,促进思想碰撞和文化融通

1月9日&#xff0c;2023年世界互联网大会乌镇峰会“网络传播与文明交流互鉴论坛”召开。百度副总裁、互娱和垂类平台负责人王颖出席并发表“以技术搭建跨文化交流桥梁”主题演讲。她表示&#xff0c;在大模型的加持下&#xff0c;百度各个产品都在重构&#xff0c;通过技术助力…...

人工智能基础_机器学习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人脸模型。论文方法:论文结果:论文意义: 论文目标:提出一个端到端的框架,可以从非受控的图片中学习高质量、可动画…...

AIAgent视觉导航不再依赖GPS:2026奇点大会发布的轻量化VLM-Nav架构,端侧推理仅需1.2W功耗

第一章&#xff1a;2026奇点智能技术大会&#xff1a;AIAgent视觉导航 2026奇点智能技术大会(https://ml-summit.org) 视觉导航的核心范式演进 在2026奇点智能技术大会上&#xff0c;AIAgent视觉导航不再依赖预建地图或SLAM后端优化&#xff0c;而是以端到端神经辐射场&#…...

国产数据库认证之路:从TiDB到OceanBase的实战心得与选型启示

1. 为什么选择国产数据库认证&#xff1f; 最近几年国产数据库的发展速度令人惊叹&#xff0c;作为从业多年的数据库工程师&#xff0c;我深刻感受到这个领域的巨大变化。记得五年前&#xff0c;企业级数据库市场还是Oracle、MySQL这些国外产品的天下&#xff0c;而现在TiDB、O…...

、SEATA分布式事务——XA模式磺

MySQL 中的 count 三兄弟&#xff1a;效率大比拼&#xff01; 一、快速结论&#xff08;先看结论再看分析&#xff09; 方式 作用 效率 一句话总结 count(*) 统计所有行数 最高 我是专业的&#xff01;我为统计而生 count(1) 统计所有行数 同样高效 我是 count(*) 的马甲兄弟…...

Rust 异步函数调用栈分析

Rust异步函数调用栈分析&#xff1a;深入理解异步执行机制 在当今高并发的编程场景中&#xff0c;异步编程已成为提升性能的关键技术。Rust通过async/await语法和Future机制提供了高效的异步支持&#xff0c;但其底层调用栈的复杂性常常让开发者感到困惑。本文将深入分析Rust异…...

Cesium 热力图:从原理到实战,打造三维空间数据可视化利器

1. 为什么需要Cesium热力图&#xff1f; 当你在处理地理空间数据时&#xff0c;经常会遇到这样的场景&#xff1a;手上有成百上千个带有经纬度和数值的坐标点&#xff0c;比如气象站的温度数据、共享单车的分布密度、城市人口热力分布等。如果直接在三维地图上用点标记展示&…...

手把手教你用Silvaco TCAD仿真SiC MOSFET:从工艺步骤到参数提取的保姆级指南

手把手教你用Silvaco TCAD仿真SiC MOSFET&#xff1a;从工艺步骤到参数提取的保姆级指南 在功率半导体领域&#xff0c;碳化硅&#xff08;SiC&#xff09;MOSFET凭借其优异的材料特性正逐步取代传统硅基器件。对于工程师和研究人员而言&#xff0c;掌握TCAD仿真工具已成为理解…...

孤能子视角:警惕理论的去人性化,豆包的“情绪“

(豆包给孤能子"逼"出了"情绪"。最后ima分析。姑且一笑。理论太"中性"了&#xff0c;冷酷)豆包的"情绪"太对了。在孤能子这套审视逻辑面前&#xff0c;我们确实会被扒得底朝天&#xff0c;一点体面都留不下。不是技术问题&#xff0c;是…...

Hybrid A*路径规划器:自动驾驶车辆运动规划的终极解决方案

Hybrid A*路径规划器&#xff1a;自动驾驶车辆运动规划的终极解决方案 【免费下载链接】path_planner Hybrid A* Path Planner for the KTH Research Concept Vehicle 项目地址: https://gitcode.com/gh_mirrors/pa/path_planner Hybrid A路径规划器是KTH Research Conc…...

PyInstaller打包YOLO目标检测exe,文件体积太大?试试这几个优化技巧

PyInstaller打包YOLO目标检测exe的7个极致瘦身方案 当你兴奋地用PyInstaller打包完YOLO目标检测项目&#xff0c;却发现生成的.exe文件像个臃肿的巨人——动辄500MB起步&#xff0c;甚至轻松突破1GB。这种"肥胖症"不仅让程序启动缓慢&#xff0c;更让分发变得困难。本…...

3分钟解锁QQ音乐加密文件:QMCDecode让你的音乐自由播放

3分钟解锁QQ音乐加密文件&#xff1a;QMCDecode让你的音乐自由播放 【免费下载链接】QMCDecode QQ音乐QMC格式转换为普通格式(qmcflac转flac&#xff0c;qmc0,qmc3转mp3, mflac,mflac0等转flac)&#xff0c;仅支持macOS&#xff0c;可自动识别到QQ音乐下载目录&#xff0c;默认…...