执行Lua脚本后一直查询不到Redis中的数据(附带问题详细排查过程,一波三折)

news2024/11/26 4:31:30

文章目录

  • 执行Lua脚本后一直查询不到Redis中的数据(附带详细问题排查过程,一波三折)
    • 问题背景
    • 问题1:Lua脚本无法切库
    • 问题2:RedisTemlate切库报错
    • 问题3:序列化导致数据不一致
    • 问题4:Lua脚本中单引号无法拼接字符串
    • 总结

执行Lua脚本后一直查询不到Redis中的数据(附带详细问题排查过程,一波三折)

这个问题坑惨我了,估计耗费了我两个小时😫,中间走了不少弯路,好在我灵光一闪+GPT给我的灵感否则就栽在这上面了

问题背景

  • 问题背景

    在使用 Redis 实现接口调用次数扣减操作时,发现响应结果一直返回的是 -1,也就是查询不到数据

    image-20230813222603833

问题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脚本不能切换数据库,还是太天真了

    image-20230813223736240

    既然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 时,默认情况下是共享连接的,因为这可以提高性能和效率。然而,如果你确实需要关闭共享连接,并为每个数据库创建一个独立的连接,也是可以的。但是需要注意一些潜在的问题:

      1. 性能影响:共享连接可以减少连接的开销,而独立连接则需要更多的资源来维护和管理。因此,关闭共享连接可能会对系统的整体性能产生一定的影响。
      2. 连接池限制:Redis 连接池有着最大连接数的限制,每个连接都占用一部分连接资源。如果为每个数据库都创建独立的连接,那么连接池中可用的连接数量将被均分,这可能导致每个数据库可用连接数的减少。
      3. 连接管理复杂性:对于每个独立的连接,你需要额外管理和维护连接的生命周期,包括创建、销毁、异常处理等。这可能增加代码的复杂性和维护成本。

      总之,关闭共享连接并为每个数据库创建独立的连接可能会带来性能和连接管理方面的一些负面影响。因此,在进行决策时,请权衡利弊,并根据具体需求和系统规模做出选择。

    • 解决方案二:新建一个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发现是有数据的,而且数据是真确的,这是什么原因呢?

    image-20230814104711299

    虽然返回结果是一样的,可能不可能Lua脚本中的值不一样呢?我继续抱着试一试的态度,执行下面的代码

    local key1 = KEYS[1]
    if key1 == '/api/name/user' then
        return 1
    end
    return 2
    

    结果意料之外的居然返回了 2,也就是说我 传入了 ’/api/name/user‘,然后从 Lua 中 返回的是 ’/api/name/user‘,但是 Lua 中两个值竟然不相等!

    我开始怀疑难道是编码的问题吗?因为之前在看微信公众号上的一篇文章,有个大佬就是因为编码(全角和半角)的问题导致查询不到数据,于是我抱着试一试的心态,测试了一下编码,有看了一下编译器使用的编码格式,发现是全局 UTF-8

    所以可以排除编码问题了,那会是什么原因呢,百思不得其解,突然灵光一闪会不会是序列化的问题,我全局为RedisTemplate配置了序列化转换器,但是我还是不敢相信,因为我在Lua脚本中返回的 retrun ARGV[1]和我查询使用的 key也就是/api/name/user是一摸一样的

    image-20230814135211981

    发现仍然照样没有查询到数据,在 Lua 脚本中返回 pathJson,发现是\"/api/name/user\",这一下又给了灵感,会不会是传入的JSON多了有个\",于是我做出如下实验

    local key1 = KEYS[1]
    if key1 == '\"/api/name/user\"' then
        return 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脚本的参数被传出来时同样会被反序列化,这个序列化和反序列化对我而言是透明的,所以导致我没有想到居然序列化了一遍,从而导致我走了好多弯路

image-20230814141308247

这里提一嘴:前面那个Lua脚本默认使用数据库 0不完全正确,如果我们在配置文件中配置了使用哪一个数据库,那么在Lua脚本中就会使用哪一个数据库,所以我们可以不用切库,也能保障 Lua脚本能够操作数据库1,所以前面的 问题1 和 问题2 不需要,根本原因在于序列化和反序列化问题

参考资料

  • ChartGPT3.5
  • Springboot整合redis切库问题

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

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

相关文章

docker安装redis7-单机版

下载redis镜像 redis镜像各版本参考&#xff1a;redis镜像 &#xff0c;这里以redis7.0.5为例 docker pull redis:7.0.5 创建挂载目录 挂载目录按照自己挂载的路径去创建对应的目录即可 mkdir -p /root/data/redis/conf(自己的挂载路径) mkdir -p /root/data/redis/data(自…

如何部署Zabbix监控服务

目录 一、zabbix概念 二、zabbix 监控原理 三、部署Zabbix服务 第一步 关闭防火墙和安全机制 第二步 获取Zabbix下载源 第三步 更换Zabbix阿里源 第四步 安装依赖环境 第五步 安装SCL服务 第六步 修改源配置文件 第七步 安装Zabbix依赖环境 第八步 安装Zabbix所需的…

【springboot启动报错】java: 错误: 无效的源发行版:17

报错截图 解决方案 第一步&#xff1a;编辑配置&#xff0c;改为想用的jdk版本 第二步&#xff1a;文件--->项目结构&#xff0c;改为对应的SDK 第三步&#xff1a;文件--->设置--->构建、执行、部署--->编译器--->Java编译器&#xff0c;修改目标字节码版本 第…

SAP 超级BOM物料语法报表(SAP配置BOM攻略七)

我需要一个报表&#xff0c;输入超级BOM&#xff0c;把其中的物料的语法相关性展现出来&#xff0c;CS12和CS03都没有这个清单功能。网上很多CS_BOM_EXPL_MAT_V2函数的使用了&#xff0c;我也来一篇我的吧。 *&----------------------------------------------------------…

Golang下载安装

目录 1. 下载压缩包 2. 解压 3. 查看SDK是否安装成功 4. 配置环境变量 5. 查看环境变量是否配置成功 1. 下载压缩包 官网下载地址&#xff1a; All releases - The Go Programming Language Windows64位选择如下下载&#xff1a; 2. 解压 解压后内容如下&#xff1a; …

对话教授丨中国石油大学(华东)理学院:课赛结合打造有学科特色的数据科学与大数据技术专业

2015 年&#xff0c;教育部公布新增“数据科学与大数据技术”专业&#xff0c;首批本科专业 2016 年入学&#xff1b;此后&#xff0c;32 所、250 所、196 所……迄今为止&#xff0c;全国各地已有 700 余所高校开设此专业。 中国石油大学&#xff08;华东&#xff09;&#x…

【日常积累】Linux之init系统学习

init系统简介: Linux 操作系统的启动首先从 BIOS 开始&#xff0c;接下来进入 boot loader&#xff0c;由 bootloader 载入内核&#xff0c;进行内核初始化。内核初始化的最后一步就是启动 pid 为 1 的 init 进程&#xff0c;这个进程是系统的第一个进程&#xff0c;它负责产生…

ROS相关技术(概念版)

重要说明&#xff1a;本文从网上资料整理而来&#xff0c;仅记录博主学习相关知识点的过程&#xff0c;侵删。 一、参考资料 ROS官网 二、相关介绍 ROS学习笔记3&#xff1a;ROS架构&#xff08;了解&#xff09; ROS介绍 1. ROS简介 ROS是一项由通信机制、开发工具、应…

(三)Unity开发Vision Pro——入门

3.入门 1.入门 本节涵盖了几个重要主题&#xff0c;可帮助您加快visionOS 平台开发速度。在这里&#xff0c;您将找到构建第一个 Unity PolySpatial XR 应用程序的分步指南的链接&#xff0c;以及 PolySpatial XR 开发时的一些开发最佳实践。 2.开发与迭代 有关先决条件、开…

打开远程桌面连接窗口

按键“windowsr”&#xff0c;调出“运行”&#xff0c;输入"mstsc"&#xff0c;然后输入用户名和密码即可&#xff0c;用户名和密码是在被远程控制的服务器windows用户名和密码&#xff0c;即可远程桌面连接成功。

为什么所有的SiC肖特基二极管都不一样

在高功率应用中&#xff0c;碳化硅&#xff08;SiC&#xff09;的许多方面都优于硅&#xff0c;包括更高的工作温度以及更高效的高频开关性能。但是&#xff0c;与硅快速恢复二极管相比&#xff0c;纯 SiC 肖特基二极管的一些特性仍有待提高。本博客介绍Nexperia&#xff08;安…

网络设备(防火墙、路由器、交换机)日志分析监控

外围网络设备&#xff08;如防火墙、路由器、交换机等&#xff09;是关键组件&#xff0c;因为它们控制进出公司网络的流量。因此&#xff0c;监视这些设备的活动有助于 IT 管理员解决操作问题&#xff0c;并保护网络免受攻击者的攻击。通过收集和分析这些设备的日志来监控这些…

【力扣每日一题】2023.8.14 合并二叉树

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 给我们合并两棵二叉树&#xff0c;合并的方式就是把对应位置的节点的值相加&#xff0c;最后把合并后的二叉树的根节点返回出去。 这类二…

如何使用Spark/Flink等分布式计算引擎做网络入侵检测

如何使用Spark/Flink等分布式计算引擎做网络入侵检测 引言16 Distributed Abnormal Behavior Detection Approach Based on Deep Belief Network and Ensemble SVM Using Spark17 Spark configurations to optimize decision tree classification on UNSW-NB1518 A dynamic spa…

低成本搭建NAS,利用HFS进行内网穿透,实现公网访问

通过HFS低成本搭建NAS&#xff0c;并内网穿透实现公网访问 文章目录 通过HFS低成本搭建NAS&#xff0c;并内网穿透实现公网访问前言1.下载安装cpolar1.1 设置HFS访客1.2 虚拟文件系统 2. 使用cpolar建立一条内网穿透数据隧道2.1 保留隧道2.2 隧道名称2.3 成功使用cpolar创建二级…

Docker-compose部署Harbor实操(含故障处理)

操作环境 Harbor主机系统类型&#xff1a;Centos7 Harbor版本&#xff1a;v2.8.3 Docker-compose版本&#xff1a;1.29.2 Docker版本&#xff1a; 一.Docker及Docker-compose环境安装 官方对Docker及Docker-compose版本有要求&#xff0c;目前我这个主机的版本不适用&#x…

14k视觉大模型综述

目录 0.导读1.背景介绍1.1基础架构1.2目标函数1.2.1对比式学习1.2.2生成式学习 1.3预训练1.3.1预训练数据集1.3.2微调1.3.3提示工程 2.基于文本提示的基础模型2.1基于对比学习的方法2.1.1基于通用模型的对比方法2.1.2基于视觉定位基础模型的方法 2.2基于生成式的方法2.3基于对比…

springboot邮件任务

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId></dependency> 依赖 配置文件 spring.mail.username1393087444qq.com spring.mail.password************* spring.mail.hos…

代码详解 —— VGG Loss

文章目录 VGG Loss 的基础概念VGG 的网络结构VGG LOSS 的代码解析参考 VGG Loss 的基础概念 VGG Loss 是content Loss中的一种。 为了评价图像的perceptual quality&#xff0c;《Perceptual losses for real time style transfer and super-resolution》 和 《GeneratingImag…

【计算机视觉 | 图像分割】arxiv 计算机视觉关于图像分割的学术速递(8 月 8 日论文合集)

文章目录 一、分割|语义相关(19篇)1.1 Mask Frozen-DETR: High Quality Instance Segmentation with One GPU1.2 AdaptiveSAM: Towards Efficient Tuning of SAM for Surgical Scene Segmentation1.3 SEM-GAT: Explainable Semantic Pose Estimation using Learned Graph Atten…