springboot+lua脚本 实现脚本缓存-evalsha

前言

可使用版本:从 Redis 2.6.0 版本开始起;可通过内置的 Lua 解释器,可以使用 EVAL 命令对 Lua 脚本进行执行。

时间复杂度:根据脚本的复杂度而定(脚本尽量简洁)。

看到网上很多文章每次使用lua 脚本 都要把脚本发送到redis 客户端 然后执行, 在高并发环境下 这个io 是很巨大的,所以我们可以提前把脚本内容提前缓存到redis 客户端 通过sha1 值去执行。

流程

spring Boot+redisTemplate 的整合和配置就不在这写了

第一步

先把lua 脚本 放在 resource 目录下

--库存key
local key1 = KEYS[1]
--事务回查key
local key2 = KEYS[2]
--扣减数量
local num = ARGV[1]
--操作redis 获取sku库存数stock
local stock = tonumber(redis.call('GET', key1))
local result = 0
--如果库存数大于等于减去的数量 则执行redis的decrby命令
--在lua中,除了nil和false,其他的值都为true,所以可用作判空
if(stock and tonumber(stock) >= tonumber(num)) then
  redis.call('DECRBY', key1, num)
  result = 1
end
--用于事务回查
redis.call('SET', key2, result)
return result
第二步

配置项

@Slf4j
@Configuration
public class RedisScriptConfig {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Bean
    public DefaultRedisScript<Long> deductStockRedisScript() {
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        //我这里的路径是resource  下的 lua 文件下的 .lua文件
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/deductStock.lua")));
        redisScript.setResultType(Long.class);
        loadRedisScript(redisScript, "deductStock.lua");
        return redisScript;
    }

    /**
     * 加载lua脚本到redis服务器
     * @param redisScript
     * @param luaName
     */
    private void loadRedisScript(DefaultRedisScript<Long> redisScript, String luaName) {
        try {
            List<Boolean> results = stringRedisTemplate.getConnectionFactory().getConnection().scriptExists(redisScript.getSha1());
            if (Boolean.FALSE.equals(results.get(0))) {
                String sha = stringRedisTemplate.getConnectionFactory().getConnection().scriptLoad(scriptBytes(redisScript));
                log.info("预加载lua脚本成功:{}, sha=[{}]", luaName, sha);
            }
        } catch (Exception e) {
            log.error("预加载lua脚本异常:{}", luaName, e);
        }
    }

    /**
     * 序列化lua脚本
     * @param script
     * @return
     */
    private byte[] scriptBytes(RedisScript<?> script) {
        return this.stringRedisTemplate.getStringSerializer().serialize(script.getScriptAsString());
    }
}
第三步

这里就是把对象注入进来 然后方法里调用即可

@Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private RedisScript<Long> deductStockRedisScript;
     
     // 在方法里调用执行
         // 第一个参数 对lua 脚本对象的调用
         // 第二个参数 是 lua 脚本中 Keys 参数
         // 第三个参数  是 lua  脚本中 ARGV 参数
         Long result = stringRedisTemplate.execute(deductStockRedisScript, keys, messageVO.getCount());

特此说明一下哈 如果你的redis 是cluster 模式也没有关系。 redisTemplate 会拿到所有的redis 客户端 去缓存脚本。 这个大家也可以自己dubug 一下 源码 看下。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值