redis+lua实现高并发库存扣减和回流,超卖和少卖场景使用。

2023-01-13 11:45:15

怎么保证执行的原子性?

Redis使用同一个Lua解释器来执行所有命令,同时,Redis保证以一种原子性的方式来执行脚本:当lua脚本在执行的时候,不会有其他脚本和命令同时执行,这种语义类似于 MULTI/EXEC。从别的客户端的视角来看,一个lua脚本要么不可见,要么已经执行完。

然而这也意味着,执行一个较慢的lua脚本是不建议的,由于脚本的开销非常低,构造一个快速执行的脚本并非难事。但是你要注意到,当你正在执行一个比较慢的脚本时,所以其他的客户端都无法执行命令。

redis+lua实现高并发库存扣减和回流过程:

用的是redis list的方式,但是在库存量较大时会占用大量的redis资源。正好现在又有一个新的需求,牵涉到库存控制,由于时间上已不允许完全重写,所以只是在原有的代码基础上简单增加了几行代码,使用redis+lua进行库存管理,其他代码完全使用现有代码不改变

核心思路如下:

1、存储时,把库存存储在数据库的基础上冗余一份到redis。由于本次需求是一个活动3个字,每个字库存独立,所以采取了hash的方式来存,尽量减少key的数量是redis使用的基本原则。

key:"butterfly:collectword:"+activityId 

field:word.getId() 

value:word.getWordNum()

2、抽奖时,先走原有代码,根据几率计算是否中奖。如果中奖,根据活动id,中奖wordid, 进行库存扣减,然后判断扣减后是否库存小于0,如果扣减后库存已小于0,并把库存再次+1,避免库存一直减下去,并返回-1,否则返回剩余库存。

如果是在java里面写这个流程,功能实现是OK的,但是在大并发下,库存就会超卖。因为java环境下,整个流程并不是一个原子操作。

利用redis+lua的原子操作特性,可以简单的避免这个问题。

代码如下:

String script="local stock= redis.call('HINCRBY',KEYS[1],ARGV[1],ARGV[2]);if stock<0 and stock>-1000 then redis.call('HINCRBY',KEYS[1],ARGV[1],ARGV[3]); return -1 else return stock end" ;

String stock=JedisClusterUtil.eval_r(script, 1, "butterfly:collectword:"+activity,word.getId(),"-1","1");

顺便对eval方法参数做一个简单的说明

第一个参数,lua脚本。

第二个参数 后面的数组里包含的key的个数

第三个参数  String ...pars   就是多个string

redis在执行lua脚本时,会把 pars分割为2个数组,一个是keys 一个是argv

分割依据是   第二个参数,把前N个参数作为keys  其他的作为argv

需要注意的是,LUA脚本里访问数组元素角标从1开始。

 

 

具体关于lua脚本的内容使用请移步至 redis命令参考–Script脚本 : http://doc.redisfans.com/script/index.html

  • 作者:JavinLu
  • 原文链接:https://blog.csdn.net/lujiawei00/article/details/109239151
    更新时间:2023-01-13 11:45:15