Lua脚本在Redis事务中的应用实践

[复制链接]
发表于 2022-9-23 11:01:51 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
使用过Redis事务的应该清楚,Redis事务实现是通过打包多条命令,单独的隔离操作,事务中的所有命令都会按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。事务中的命令要么全部被执行,要么全部都不执行(原子操作)。但其中有命令因业务原因执行失败并不会阻断后续命令的执行,且也无法回滚已经执行过的命令。如果想要实现和MySQL一样的事务处理可以使用Lua脚本来实现,Lua脚本中可实现简单的逻辑判断,执行中止等操作。
1 初始Lua脚本

Lua是一个小巧的脚本语言,Redis 脚本使用 Lua 解释器来执行脚本。 Reids 2.6 版本通过内嵌支持 Lua 环境。执行脚本的常用命令为 EVAL。编写Lua脚本就和编写shell脚本一样的简单。Lua语言详细教程参见
示例:
  1. --[[
  2.     version:1.0
  3.     检测key是否存在,如果存在并设置过期时间
  4.     入参列表:
  5.         参数个数量:1
  6.         KEYS[1]:goodsKey 商品Key
  7.     返回列表code:
  8.         +0:不存在
  9.         +1:存在
  10. --]]
  11. local usableKey = KEYS[1]
  12. --[ 判断usableKey在Redis中是否存在 存在将过期时间延长1分钟 并返回是否存在结果--]
  13. local usableExists = redis.call('EXISTS', usableKey)
  14. if (1 == usableExists) then
  15.     redis.call('PEXPIRE', usableKey, 60000)
  16. end
  17. return { usableExists }
复制代码

  • 示例代码中redis.call(), 是Redis内置方法,用与执行redis命令
  • if () then end 是Lua语言基本分支语法
  • KEYS 为Redis环境执行Lua脚本时Redis Key 参数,如果使用变量入参使用ARGV接收
  • “—”代表单行注释 “—[[ 多行注释 —]]”
2 实践应用

2.1 需求分析

经典案例需求:库存量扣减并检测库存量是否充足。

基础需求分析:商品当前库存量>=扣减数量时,执行扣减。商品当前库存量 connection.scriptLoad(script.getBytes()));}[/code]脚本执行
  1. //利用Watch 命令乐观乐特性,减少锁竞争所损耗的性能
  2. public boolean init(InitStockCallback initStockCallback, InitOperationData initOperationData) {
  3. //SessionCallback 会话级Rdis事务回调接口 针对于operations所有操作将在同一个Redis tcp连接上完成
  4. List<Object> result = stringRedisTemplate.execute(new SessionCallback<List<Object>>() {
  5.             public List<Object> execute(RedisOperations operations) {
  6.                 Assert.notNull(operations, "operations must not be null");
  7. //Watch 命令用于监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断
  8. //当出前并发初始化同一个商品库存量时,只有一个能成功
  9.                 operations.watch(initOperationData.getWatchKeys());
  10.                 int initQuantity;
  11.                 try {
  12. //查询DB商品库存量
  13.                     initQuantity = initStockCallback.getInitQuantity(initOperationData);
  14.                 } catch (Exception e) {
  15.                     //异常后释放watch
  16.                     operations.unwatch();
  17.                     throw e;
  18.                 }
  19. //开启Reids事务
  20.                 operations.multi();
  21. //setNx设置商品库存量
  22.                 operations.opsForValue().setIfAbsent(initOperationData.getGoodsKey(), String.valueOf(initQuantity));
  23. //设置商品库存量 key 过期时间
  24.                 operations.expire(initOperationData.getGoodsKey(), Duration.ofMinutes(60000L));
  25. ///执行事事务
  26.                 return operations.exec();
  27.             }
  28.         });
  29. //判断事务执行结果
  30.         if (!CollectionUtils.isEmpty(result) && result.get(0) instanceof Boolean) {
  31.             return (Boolean) result.get(0);
  32.         }
  33.         return false;
  34.     }
复制代码
3 总结

Redis在小数据操作并发可达到10W,针对与业务中对资源强校验且高并发场景下使用Redis配合Lua脚本完成简单逻辑处理抗并发量是个不错的选择。
注:Lua脚本逻辑尽量简单,Lua脚本实用于耗时短且原子操作。耗时长影响Redis服务器性能,非原子操作或逻辑复杂会增加于脚本调试与维度难度。理想状态是将业务用Lua脚本包装成一个如Redis命令一样的操作。
作者:王纯

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表