重写RedisTemplate后在lua脚本中传递参数不需要二次转换

问题

由于我在lua脚本中需要去redis取数据做判断,这里传递了两个参数到lua脚本中,但一直提示我类型转换不对,通过debug查看发现是我使用了RedisTemplate并重写了转换器,然后我传参时进行了二次转换导致最后Redis拼接后的key是错的

原本的代码

java 复制代码
public void seckill_redisson_async(Long userId, Long productId) {
        Long execute = redisTemplate.execute(
                SECKILL_SCRIPT,
                Collections.emptyList(),
                productId.toString(), userId.toString());
        if (execute != 0) {
            throw new BusinessException(execute == 1 ? "库存不足" : "请勿重复下单");
        }
        seckillSupport.createOrderAsync(userId, productId);
    }

--lua脚本
local productId = ARGV[1]
local userId = ARGV[2]

local stock_key = "product:stock:"..productId
local order_key = "product:seckill:"..productId
if(tonumber(redis.call('get', stock_key)) <= 0) then
    return 1
end
if (redis.call('sismember', order_key, userId) == 1) then
    return 2
end
redis.call('incrby', stock_key, -1)
redis.call('sadd', order_key, userId)
return 0

这里实际上lua脚本在拼接key时:

  • 期待结果:
    • stock_key:product:stock:1
    • order_key:product:seckill:1
  • 实际结果:
    • stock_key:product:stock:"1"
    • order_key:product:seckill:"1"

为什么会多了一个引号,这里就要看RedisTemplate底层的序列化器了

RedisTempate和StringRedisTemplate

由于Redis底层存储的是二进制数据,在Spring使用中,直接操作redis会发现键值都是二进制数据,于是我们会选择对RedisTemplate进行重写或者使用StringRedisTemplate,但在执行lua脚本过程中,由于我们使用不同的方式去操作redis,那针对参数的传入也会有所不同,通过下面RedisTemplateStringRedisTemplate源码来进行分析

RedisTemplate

java 复制代码
@Configuration
public class RedisConfig {

    /**
     * redisTemplate在存储数据时会将数据转换成字节数组进行存储,因此使用实体存储时需要实现serialization接口
     * 使用转换器,redisTemplate在存储时将实体转换成字符串写入,在接收时自动转换成对象
     * */

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        //创建RedisTemplate对象
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        //设置连接工厂
        template.setConnectionFactory(redisConnectionFactory);
        //创建JSON序列化工具
        GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        //设置key的序列化
        template.setKeySerializer(RedisSerializer.string());
        template.setHashKeySerializer(RedisSerializer.string());
        //设置value的序列化
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        //返回
        return template;
    }
}

StringRedisTemplate源码

这一部分是StringRedisTemplate键值在构造时使用RedisSerializer转换器序列化为字符串

java 复制代码
public class StringRedisTemplate extends RedisTemplate<String, String> {
    public StringRedisTemplate() {
        this.setKeySerializer(RedisSerializer.string());
        this.setValueSerializer(RedisSerializer.string());
        this.setHashKeySerializer(RedisSerializer.string());
        this.setHashValueSerializer(RedisSerializer.string());
    }

    public StringRedisTemplate(RedisConnectionFactory connectionFactory) {
        this();
        this.setConnectionFactory(connectionFactory);
        this.afterPropertiesSet();
    }
    ...

分析

RedisTemplate中采用了GenericJackson2JsonRedisSerializer 序列化器,它会自动帮我们把实体转换成json形式存入redis中,并在读取时通过@Class标志转回对应实体,因此我们在使用RedisTemplate可以传入任何类型即Object,由序列化器帮我们完成对应的转换操作

但是在StringRedisTemplate中,由于底层以字符串形式传入,因此我们只能传入String类型

那在lua脚本使用过程中,使用StrimgRedisTemplate需要将传入的参数进行字符串形式转换,参考以下代码:

lua 复制代码
--lua脚本
local userId = ARGV[1]
return userId
java 复制代码
Long result = stringRedisTemplate.execute(
                SCRIPT,//lua脚本
                Collections.emptyList(),//key列表
                userId.toString()//Object类型值,但由于StringRedisTemplate的value是String类型,因此这里需要进行字符串转换
);

RedisTemplate由于采用了JSON序列化器,因此不需要进行字符串转换,直接传入就可以

java 复制代码
Long result = redisTemplate.execute(
                SCRIPT,//lua脚本
                Collections.emptyList(),//key列表
                userId//Object类型值,直接传

假设这里Long userId = 1L,这里如果再次进行字符串转换之后,Redis进行值设置时会变成""1"",这里变成了双层双引号嵌套,lua脚本在读取值时就会报错

总结

lua中进行参数传递时,需要查看自己操作redis是哪种类型,如果是重写了底层并设置了相应的转换器,则不需要再进行二次转换

相关推荐
·薯条大王14 小时前
经济实惠玩云服务器|一台云服务器多人共用,子账号配置教程
java·linux·运维·服务器·汇编·c++·python
ZJH__GO17 小时前
网络编程v4pro--实现聊天室文件传输功能
java·服务器·网络·计算机网络
程序员黑豆17 小时前
Windows 系统 Java 环境变量配置全攻略:解决“不是内部或外部命令”
java·前端·ai编程
命运之光17 小时前
【C语言完整代码】就诊信息管理系统
java·c语言·开发语言
凤山老林17 小时前
Spring Boot @Async 线上实战:从默认配置到生产级线程池治理
java·spring boot·后端
marvelyu18 小时前
每天10分钟学会OceanBase系列(Day 20):跨机房容灾实战——构建多数据中心高可用架构
java·大数据·数据库
AI人工智能+电脑小能手19 小时前
大白话说Java设计模式-04-工厂方法模式(业务实战篇)
java·设计模式·工厂方法模式·架构设计·代码解耦
BUG指挥官19 小时前
Sa-Token和Spring Security对比
java·后端·spring
小当家.10520 小时前
深入理解 ReAct Agent:从原理到 Java 实战
java·react.js·agent·react·架构设计·agent设计
geminigoth20 小时前
Spring AI Alibaba 入门开发一(备份)
java·人工智能·spring