欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-06-22 20:28:01
怎么保证执行的原子性?Redis使用同一个Lua解释器来执行所有命令,同时,Redis保证以一种原子性的方式来执行脚本:当lua脚本在执行的时候,不会有其他脚本和命令同时执行,这种语义类似于 MULTI/EXEC。从别的客户端的视角来看,一个lua脚本要么不可见,要么已经执行完。然而这也意味着,执行一个较慢的lua脚本是不建议的,由于脚本的开销非常低,构造一个快速执行的脚本并非难事。但是你要注意到,当你正在执行一个比较慢的脚本时,所以其他的客户端都无法执行命令。redis+lua实现高并发库存扣...

怎么保证执行的原子性?

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

本文地址:https://blog.csdn.net/lujiawei00/article/details/109239151