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

这篇具有很好参考价值的文章主要介绍了执行Lua脚本后一直查询不到Redis中的数据(附带问题详细排查过程,一波三折)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

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

问题背景

  • 问题背景

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

    执行Lua脚本后一直查询不到Redis中的数据(附带问题详细排查过程,一波三折),Bug记录,lua,redis,开发语言

问题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脚本后一直查询不到Redis中的数据(附带问题详细排查过程,一波三折),Bug记录,lua,redis,开发语言

    既然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发现是有数据的,而且数据是真确的,这是什么原因呢?

    执行Lua脚本后一直查询不到Redis中的数据(附带问题详细排查过程,一波三折),Bug记录,lua,redis,开发语言

    虽然返回结果是一样的,可能不可能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是一摸一样的

    执行Lua脚本后一直查询不到Redis中的数据(附带问题详细排查过程,一波三折),Bug记录,lua,redis,开发语言

    发现仍然照样没有查询到数据,在 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脚本的参数被传出来时同样会被反序列化,这个序列化和反序列化对我而言是透明的,所以导致我没有想到居然序列化了一遍,从而导致我走了好多弯路

执行Lua脚本后一直查询不到Redis中的数据(附带问题详细排查过程,一波三折),Bug记录,lua,redis,开发语言

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

参考资料文章来源地址https://www.toymoban.com/news/detail-648071.html

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

到了这里,关于执行Lua脚本后一直查询不到Redis中的数据(附带问题详细排查过程,一波三折)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 在 Redis 中使用 Lua 脚本执行复杂操作和事务

    在 Redis 中使用 Lua 脚本执行复杂操作和事务 Redis 作为一个高性能的键值存储数据库,它的强大功能远不止于简单的数据存储和检索。Redis 自 2.6 版本起引入了对 Lua 脚本的支持,这意味着你可以在 Redis 服务器上直接运行 Lua 脚本。这一功能为执行复杂的操作和事务提供了强大

    2024年01月25日
    浏览(51)
  • Redis 读写分离 使用redisTemplate执行lua脚本时,报错处理

    项目配置 redis架构 1主2从3哨兵模式 采用了读写分离模式 springboot使用 luttuce 项目使用redisTemplate执行lua脚本 测试代码 大致意思是说 在从节点上执行 写操作(实际上写操作是在lua脚本里面的) 为什么执行lua脚本会只走从节点? 在主从模式下,框架自身应该是可以区分读和写

    2024年02月17日
    浏览(50)
  • 【Redis 开发】分布式锁中的常见问题和Lua脚本

    分布式锁中我们设置的过期时间: 如果有一个线程获取锁之后在进行操作时,到达了锁的过期时间,之后就会有别的线程获得锁,如果这时,第一个线程执行完成后释放锁,就会将第二个锁的线程删除 针对这个情况如何改进: 在获取锁时存入线程标示(可以用UUID) 在释放

    2024年04月28日
    浏览(46)
  • Redis使用Lua脚本和Redisson来保证库存扣减中的原子性和一致性

    背景:最近有社群技术交流的同学,说面试被问到商品库存扣减的问题。我大概整理了一下内容,方便大家理解。 其实无外乎就是分布式锁和Redis命令的原子性问题 。 在分布式系统中,保证数据的原子性和一致性是一个关键问题。特别是在库存扣减等场景中,确保操作的原

    2024年02月13日
    浏览(72)
  • redis 无占用 两种方式 清除大批量数据 lua脚本

    redis存储了很多无用的key,占用了大量内存,需要清除 第一种 (颗粒度较大) lua脚本,删除某些规则的key,输入删除的key,返回删除的符合规则的key的数量 弊端:颗粒度比较大,发送一个lua脚本去执行,会占用较多时间,堵塞其他redis命令 java代码 这样直接删除,因为规则有很

    2024年04月28日
    浏览(38)
  • springboot+redis+mysql+quartz-通过Java操作jedis定时使用lua脚本获取缓存数据并更新数据库

    springboot+redis+mysql+quartz-通过Java操作jedis定时使用lua脚本获取缓存数据并更新数据库 代码讲解:7.1点赞功能-定时持久化到数据库-Java整合lua_哔哩哔哩_bilibili https://www.bilibili.com/video/BV1ZX4y1H7JT/ 代码: blogLike_schedule/like07 · xin麒/XinQiUtilsOrDemo - 码云 - 开源中国 (gitee.com) https://gitee

    2024年02月13日
    浏览(55)
  • Redis入门 - Lua脚本

    原文首更地址,阅读效果更佳! Redis入门 - Lua脚本 | CoderMast编程桅杆 https://www.codermast.com/database/redis/redis-scription.html Redis 脚本使用 Lua 解释器来执行脚本。 Redis 2.6 版本通过内嵌支持 Lua 环境。执行脚本的常用命令为 EVAL。 Eval 命令的基本语法如下: EVAL script numkeys key [key ...]

    2024年02月09日
    浏览(42)
  • Redis之Lua脚本

    目录 Lua脚本 编写Lua脚本  springboot整合redis使用lua Lua脚本        Redis在2.6推出了脚本功能,允许开发者使用Lua语言编写脚本传到Redis中执行。使用脚本的好处如下: 1. 减少网络开销:本来5次网络请求的操作,可以用一个请求完成,原先5次请求的逻辑放在redis服务器上完成。使

    2024年01月23日
    浏览(38)
  • Redis之Lua脚本讲解

    当涉及 Lua 编程时,以下是对前述12个关键概念的详细说明,附带Lua代码示例以帮助更深入了解这门编程语言 1.1.1 注释 注释在 Lua 中用于添加说明和注解。单行注释以 -- 开始,多行注释则使用 --[[ ... ]] 。 1.1.2 变量 变量在 Lua 中无需显式声明类型。使用 local 创建局部变

    2024年02月08日
    浏览(32)
  • 使用lua脚本操作redis

    redis中实现事务有两种方法: 1.WATCH监视键的变动,然后MULTI开始事务,EXEC提交事务 WATCH key [key…]:监视一个或多个键,如果在事务执行之前被修改,则事务被打断。 MULTI:标记一个事务的开始。 EXEC:执行事务中的所有命令。 DISCARD:取消一个事务,放弃执行事务中的所有命

    2024年02月16日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包