执行Lua脚本后一直查询不到Redis中的数据(附带问题详细排查过程,一波三折)
文章目录
- 执行Lua脚本后一直查询不到Redis中的数据(附带详细问题排查过程,一波三折)
- 问题背景
- 问题1:Lua脚本无法切库
- 问题2:RedisTemlate切库报错
- 问题3:序列化导致数据不一致
- 问题4:Lua脚本中单引号无法拼接字符串
- 总结
执行Lua脚本后一直查询不到Redis中的数据(附带详细问题排查过程,一波三折)
这个问题坑惨我了,估计耗费了我两个小时😫,中间走了不少弯路,好在我灵光一闪+GPT给我的灵感否则就栽在这上面了
问题背景
-
问题背景
在使用 Redis 实现接口调用次数扣减操作时,发现响应结果一直返回的是 -1,也就是查询不到数据
问题1:Lua脚本无法切库
-
问题排查过程1:
经过一段排查,加上GPT的提示信息,我快速定位到了是可能是参数的问题,但是通过 Debug 断点调试,我可以百分百肯定这个参数肯定没有问题;然后我又到 redis-cli 执行原生的 Redis 指令,发现也查不到数据!(○´・д・)ノ,懵逼了,捣鼓了半天突然想起来有没有可能是这个数据库中压根没有数据,于是我使用
keys *
指令查看数据库中是否有数据,结果发现有数据,但是压根就没有我预热的数据!!!这是什么原因呢?一看 REP 就恍然大悟了,我操作的数据库1,但是由于我配置文件中使用的是数据库 0,导致我预热的数据 都在 数据库1中,而 Lua脚本默认操作的数据库0,那么问题就简单了,直接使用redis('select',1)
这条指令切换一下数据库不久OK了吗(我真聪明🤭)然后我先在redis-cli上实验,发现的确是这个的原因,嘿嘿问题成功解决了?(你不会以为这就完了吧🤣) -
问题原因:Lua脚本默认使用的数据库0,我的数据在数据库1中
-
问题解决
在执行Lua脚本前,先切换一下数据库
redis('SELECT', 1)
==结果发现在 Lua 脚本中添加了
redis('SELECT', 1)
之后压根就没有用!==┭┮﹏┭┮只能继续排查问题,
-
问题排查过程2
在lua脚本中查询数据前,加上
redis('select',1)
,结果发现嘿嘿还是返回-1!这下彻底懵逼了,结果经过询问ChartGPT,发现:Lua脚本不能切换数据库,还是太天真了既然Lua脚本无法完成切库,那么就需要使用 RedisTemplate 进行切库
-
问题解决:
RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();connection.select(1);
你不会以为这么快就解决了这个问题吧!
问题2:RedisTemlate切库报错
-
问题排查过程2:
redisTemplate
切库引发的新问题:在添加了上面代码后,结果又报错Selecting a new database not supported due to shared connection. Use separate ConnectionFactorys to work with multiple databases
.在此询问GPT,发现是由于 Redis 的共享连接限制,无法直接在同一个连接上切换到不同的数据库。如果你需要在执行 Lua 脚本之前切换 Redis 数据库,可以尝试使用不同的连接工厂来处理多个数据库
-
问题解决
-
解决方案一:直接关闭RedisTemplate的共享连接(不推荐)
详情请参考这篇文章:Springboot整合redis切库问题
在使用 Spring Data Redis 时,默认情况下是共享连接的,因为这可以提高性能和效率。然而,如果你确实需要关闭共享连接,并为每个数据库创建一个独立的连接,也是可以的。但是需要注意一些潜在的问题:
- 性能影响:共享连接可以减少连接的开销,而独立连接则需要更多的资源来维护和管理。因此,关闭共享连接可能会对系统的整体性能产生一定的影响。
- 连接池限制:Redis 连接池有着最大连接数的限制,每个连接都占用一部分连接资源。如果为每个数据库都创建独立的连接,那么连接池中可用的连接数量将被均分,这可能导致每个数据库可用连接数的减少。
- 连接管理复杂性:对于每个独立的连接,你需要额外管理和维护连接的生命周期,包括创建、销毁、异常处理等。这可能增加代码的复杂性和维护成本。
总之,关闭共享连接并为每个数据库创建独立的连接可能会带来性能和连接管理方面的一些负面影响。因此,在进行决策时,请权衡利弊,并根据具体需求和系统规模做出选择。
-
解决方案二:新建一个RedisTemplate
为了提高代码的复用性,我就打算利用 单例模式+原型模式 对IOC容器中的RedisTemplate进行一个拷贝,然后将这个新的RedisTemplate来切库,相对于方案一要更加方便
下面是代码:
package com.ghp.admin.redis;import com.ghp.common.utils.BeanConvertorUtils; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.core.RedisTemplate;/*** @author ghp* @title* @description*/ public class RedisUtils {private RedisTemplate redisTemplate;private static RedisUtils instance;static {instance = new RedisUtils();}public RedisUtils getInstance(RedisTemplate redisTemplate){this.redisTemplate = redisTemplate;return instance;}/*** 创建一个新的RedisTemplate,并且切换数据库* @param databaseIndex* @return*/public RedisTemplate<String, String> createRedisTemplate(int databaseIndex) {// 进行拷贝(这里是是使用自己封装的工具类,你可以选择使用Spring框架自带的拷贝工具类)RedisTemplate newRedisTemplate = BeanConvertorUtils.copyBean(redisTemplate,RedisTemplate.class);RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();connection.select(databaseIndex);return newRedisTemplate;} }
看上去好像已经成功解决了,但是还没完,经过测试发现执行 Lua 脚本后仍然无法从Redis中查询到数据库w(゚Д゚)w
这些我又陷入了懵逼中
-
问题3:序列化导致数据不一致
-
问题排查过程3:
我开始陷入怀疑当中,因为我在 redis-cli 中经过切库后,发现执行指令是可以查询到数据的,难道是切库失败了?经过检验发现切库是成功的,因为我在 Lua 脚本中执行硬编码
local leftNum = redis.call('HGET', 'cache:interface:pathMethod:', '/api/name/user-POST')
是的的确确查询到了数据的!我开始怀疑是不是我参数传递错了,但是经过
return leftNum
发现是有数据的,而且数据是真确的,这是什么原因呢?虽然返回结果是一样的,可能不可能Lua脚本中的值不一样呢?我继续抱着试一试的态度,执行下面的代码
local key1 = KEYS[1] if key1 == '/api/name/user' thenreturn 1 end return 2
结果意料之外的居然返回了 2,也就是说我 传入了 ’/api/name/user‘,然后从 Lua 中 返回的是 ’/api/name/user‘,但是 Lua 中两个值竟然不相等!
我开始怀疑难道是编码的问题吗?因为之前在看微信公众号上的一篇文章,有个大佬就是因为编码(全角和半角)的问题导致查询不到数据,于是我抱着试一试的心态,测试了一下编码,有看了一下编译器使用的编码格式,发现是全局
UTF-8
所以可以排除编码问题了,那会是什么原因呢,百思不得其解,突然灵光一闪会不会是序列化的问题,我全局为RedisTemplate配置了序列化转换器,但是我还是不敢相信,因为我在Lua脚本中返回的
retrun ARGV[1]
和我查询使用的 key也就是/api/name/user
是一摸一样的发现仍然照样没有查询到数据,在 Lua 脚本中返回 pathJson,发现是
\"/api/name/user\"
,这一下又给了灵感,会不会是传入的JSON多了有个\"
,于是我做出如下实验local key1 = KEYS[1] if key1 == '\"/api/name/user\"' thenreturn 1 end return 2
惊奇的发现这回终于返回 1,也就是说传入的 path 是 竟然是
\"/api/name/user\"
(结果很震惊,因为之前我在Lua脚本中返回path时,结果就是/api/name/user
,返回的结果根本没有"
),我猜测应该是RedisTemplate在处理Lua的返回结果时会多做一层序列化,登录参数的传递过程中需要经过一层序列化!至此问题终于解决了(你不会以为这就成功解决了吧) -
解决方案
在接收参数后进行预处理,也就是去掉传入参数中多余的
”
local key1 = string.gsub(KEYS[1], "\"", "") -- cache:interface:pathMethod: local key2 = string.gsub(KEYS[2], "\"", "") -- cache:interface:leftNum: -- 获取hashKey local path = string.gsub(ARGV[1], "\"", "") local method = string.gsub(ARGV[2], "\"", "") local userId = string.gsub(ARGV[3], "\"", "")
问题4:Lua脚本中单引号无法拼接字符串
-
问题排查4
后面我改造了代码,发现仍然不成功!!!😫 ┭┮﹏┭┮
经过测试,我发现
local key1 = path .. '-' .. method return key1
发现只会返回
-
前面的字符串 也就是 path,如果调换path和method的未知,那么只会返回method,我又陷入了沉思。后面我突然想起来了,是Lua中双引号和单引号的区别,使用
..
运算符可以用来拼接字符串。这个运算符可以连接两个字符串(或者将其他数据类型转换为字符串后连接)。然而,..
运算符只能用于连接双引号字符串。单引号字符串在 Lua 中表示字符,而不是字符串。如果你试图使用..
运算符连接两个单引号字符串,会得到一个语法错误。 -
解决方法
local key1 = tostring(path) .. "-" .. tostring(method) return key1
成功解决了。如果你对Lua感兴趣的可以参考这篇文章:Lua快速入门笔记_知识汲取者的博客-CSDN博客
总结
终于解决了这个问题,感觉好舒爽😄
总的来说,我遇到的问题是 Java 代码使用 RedisTemplate.execute
执行 Lua脚本的时候,由于我配置了全局序列化,所以导致 传入Lua脚本中的参数 会先被序列化,而Lua脚本的参数被传出来时同样会被反序列化,这个序列化和反序列化对我而言是透明的,所以导致我没有想到居然序列化了一遍,从而导致我走了好多弯路
这里提一嘴:前面那个Lua脚本默认使用数据库 0不完全正确,如果我们在配置文件中配置了使用哪一个数据库,那么在Lua脚本中就会使用哪一个数据库,所以我们可以不用切库,也能保障 Lua脚本能够操作数据库1,所以前面的 问题1 和 问题2 不需要,根本原因在于序列化和反序列化问题
参考资料
- ChartGPT3.5
- Springboot整合redis切库问题
相关文章:

执行Lua脚本后一直查询不到Redis中的数据(附带问题详细排查过程,一波三折)
文章目录 执行Lua脚本后一直查询不到Redis中的数据(附带详细问题排查过程,一波三折)问题背景问题1:Lua脚本无法切库问题2:RedisTemlate切库报错问题3:序列化导致数据不一致问题4:Lua脚本中单引号…...

[高光谱]PyTorch使用CNN对高光谱图像进行分类
项目原地址: Hyperspectral-Classificationhttps://github.com/eecn/Hyperspectral-ClassificationDataLoader讲解: [高光谱]使用PyTorch的dataloader加载高光谱数据https://blog.csdn.net/weixin_37878740/article/details/130929358 一、模型加载 在…...

jmeter获取mysql数据
JDBC Connection Configuration Database URL: jdbc:mysql:// 数据库地址 /库名 JDBC Driver class:com.mysql.jdbc.Driver Username:账号 Password:密码 JDBC Request 字段含义 字段含义 Variable Name Bound to Pool 数据库连接池配置…...

Dedecms V110最新版RCE---Tricks
前言 刚发现Dedecms更新了发布版本,顺便测试一下之前的day有没有修复,突然想到了新的tricks去实现RCE。 文章发布的时候估计比较晚了,一直没时间写了。 利用 /uploads/dede/article_string_mix.php /uploads/dede/article_template_rand.…...

CTFshow 限时活动 红包挑战7、红包挑战8
CTFshow红包挑战7 写不出来一点,还是等了官方wp之后才复现。 直接给了源码 <?php highlight_file(__FILE__); error_reporting(2);extract($_GET); ini_set($name,$value);system("ls ".filter($_GET[1])."" );function filter($cmd){$cmd…...

Redis使用Lua脚本和Redisson来保证库存扣减中的原子性和一致性
文章目录 前言1.使用SpringBoot Redis 原生实现方式2.使用redisson方式实现3. 使用RedisLua脚本实现3.1 lua脚本代码逻辑 3.2 与SpringBoot集成 4. Lua脚本方式和Redisson的方式对比5. 源码地址6. Redis从入门到精通系列文章7. 参考文档 前言 背景:最近有社群技术交…...

【从零开始学Kaggle竞赛】泰坦尼克之灾
目录 0.准备1.问题分析挑战流程数据集介绍结果提交 2.代码实现2.1 加载数据2.1.1 加载训练数据2.1.2 加载测试数据 2.2 数据分析2.3 模型建立与预测 3.结果提交 0.准备 注册kaggle账号后,进入titanic竞赛界面 https://www.kaggle.com/competitions/titanic 进入后界…...
输出无重复的3位数和计算无人机飞行坐标
编程题总结 题目一:输出无重复的3位数 题目描述 从{1,2,3,4,5,6,7,8,9}中随机挑选不重复的5个数字作为输入数组‘selectedDigits’,能组成多少个互不相同且无重复数字的3位数?请编写程》序,从小到大顺序,以数组形式输出这些3位…...
muduo 29 异步日志
目录 Muduo双缓冲异步日志模型: 异步日志实现: 为什么要实现非阻塞的日志...
Qt 对象序列化/反序列化
阅读本文大概需要 3 分钟 背景 日常开发过程中,避免不了对象序列化和反序列化,如果你使用 Qt 进行开发,那么有一种方法实现起来非常简单和容易。 实现 我们知道 Qt 的元对象系统非常强大,基于此属性我们可以实现对象的序列化和…...
从零学算法(非官方题库)
输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构) B是A的子结构, 即 A中有出现和B相同的结构和节点值。 例如: 给定的树 A:3/ \4 5/ \1 2给定的树 B:4 / 1返回 true,因为 B 与 A 的一个子树拥有相…...
Java # JVM内存管理
一、运行时数据区域 程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区、运行时常量池、直接内存 二、HotSpot虚拟机对象 对象创建: 引用检查类加载检查分配内存空间:指针碰撞、空闲列表分配空间初始化对象信息设置(对象头内࿰…...
大疆第二批笔试复盘
大疆笔试复盘(8-14) 笔试时候的状态和下来复盘的感觉完全不一样,笔试时脑子是懵的。 (1)输出无重复三位数 题目描述 从 { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 } \left \{ 1,2,3,4,5,6,7,8,9 \right \...
【Linux】磁盘或内存 占用比较高要怎么排
当 Linux 磁盘空间满了时 请注意,在进行任何删除操作之前,请确保你知道哪些文件可以安全删除,并备份重要文件,以免意外丢失数据。当 Linux 磁盘空间满了时,可以按照以下步骤进行排查: 检查磁盘使用情况&…...

解决xss转义导致转码的问题
一、xss简介 人们经常将跨站脚本攻击(Cross Site Scripting)缩写为CSS,但这会与层叠样式表(Cascading Style Sheets,CSS)的缩写混淆。因此,有人将跨站脚本攻击缩写为XSS。跨站脚本攻击ÿ…...

numba 入门示例
一维向量求和: C A B 在有nv 近几年gpu的ubuntu 机器上, 环境预备: conda create -name numba_cuda_python3.10 python3.10 conda activate numba_cuda_python3.10conda install numba conda install cudatoolkit conda install -c nvi…...

BUUCTF 还原大师 1
题目描述: 我们得到了一串神秘字符串:TASC?O3RJMV?WDJKX?ZM,问号部分是未知大写字母,为了确定这个神秘字符串,我们通过了其他途径获得了这个字串的32位MD5码。但是我们获得它的32位MD5码也是残缺不全,E903???4D…...
自定义hook之首页数据请求动作封装 hooks
本例子实现了自定义hook之首页数据请求动作封装 hooks,具体代码如下 export type OrganData {dis: Array<{ disease: string; id: number }>;is_delete: number;name: string;organ_id: number;parent_id: number;sort: number; }; export type SwiperData …...

2023上半年京东手机行业品牌销售排行榜(京东数据平台)
后疫情时代,不少行业都迎来消费复苏,我国智能手机市场在今年上半年也实现温和的复苏,手机市场的出货量回暖。 根据鲸参谋平台的数据显示,2023年上半年,京东平台上手机的销量为2830万,环比增长约4%…...
lodash之cloneDeep()源码阅读笔记
lodash之cloneDeep()源码阅读笔记 基本上都在写业务代码,没有机会写库,还是想了解一下lodash的库源码是怎么样的,平时用的最多的就是cloneDeep()方法了,终于有空详细看看其中的源码。 本文基于lodash5.0.0版本的源码进行阅读。 /…...

SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...

边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...

python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...

HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...

Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...