1、定义LUA脚本
/**
* 扣减库存Lua脚本
* 库存(stock)-1:表示不限库存
* 库存(stock)0:表示没有库存
* 库存(stock)大于0:表示剩余库存
* <p>
* 库存key
* -3:库存未初始化
* -2:库存不足
* -1:不限库存
* 大于等于0:剩余库存(扣减之后剩余的库存)
* redis缓存的库存(value)是-1表示不限库存,直接返回1
*/
public static final String DEDUCT_STOCK_LUA =
"if (redis.call('exists', KEYS[1]) == 1) then" +
" local stock = tonumber(redis.call('get', KEYS[1]));" +
" local num = tonumber(ARGV[1]);" +
" if (stock == -1) then" +
" return -1;" +
" end;" +
" if (stock >= num) then" +
" return redis.call('incrby', KEYS[1], 0 - num);" +
" end;" +
" return -2;" +
" end;" +
" return -3;";
通过分析以上lua脚本,可以看出脚本的执行流程。
第一步:判断了一下KEYS[1]是否存在,不存在则返回-3,-3标识库存未初始化;
第二步:KEYS[1]值存在,则获取到KEYS[1]的值stock,即库存数量,在获取到ARGV[1],将其转化为数字;
第三步:判断一下stock库存数量是否是-1,-1表示不限制库存数量;
第四步:如果库存数量不为-1,那么就判断下stock库存数量是否是大于等于要扣减的库存数,如果大于等于,表示库存是充足的可以进行扣减操作,然后返回扣减后当前库存数量;
第五步:如果小于0,则库存不足,不能扣减,此时返回-2,表示库存不足。
在lua脚本中,包含了KEYS[1]与ARGV[1],分别代表两个入参,一个是操作的key,一个是操作的变量值(库存扣减数量)。
/**
* 修改库存数量
* 如果缓存存在,则获取库存数量与修改数量
* <p>
* 1、如果修改数量大于0,则增加库存数量
* 2、如果修改数量小于0,则扣减库存
* 2.1、库存数量大于扣减的数量,则直接扣减
* 2.2、库存数量小于扣减的数量,则库存不足,将库存置为0
*/
public static final String UPDATE_STOCK_LUA =
"if (redis.call('exists', KEYS[1]) == 1) then" +
" local stock = tonumber(redis.call('get', KEYS[1]));" +
" local num = tonumber(ARGV[1]);" +
" if (num > 0) then" +
" return redis.call('incrby', KEYS[1], num);" +
" end;" +
" if (num < 0 and stock >= -num) then" +
" return redis.call('incrby', KEYS[1], num);" +
" end;" +
" if (num < 0 and stock < -num) then" +
" return redis.call('incrby', KEYS[1], 0 - stock);" +
" end;" +
" return -2;" +
" end;" +
" return -3;";
2、使用LUA脚本
private Long deductStock(String key, int num) {
// 脚本里的KEYS参数
List<String> keys = new ArrayList<String>();
keys.add(key);
// 脚本里的ARGV参数
List<String> args = new ArrayList<String>();
args.add(Integer.toString(num));
// 将sha保存到Redis中缓存起来
String sha = redisClient.get(LuaScriptEnum.DEDUCT_STOCK_LUA.getKey());
if (StringUtils.isEmpty(sha)) {
sha = redisClient.scriptLoad(LuaScriptEnum.DEDUCT_STOCK_LUA.getScript());
redisClient.set(LuaScriptEnum.DEDUCT_STOCK_LUA.getKey(), sha);
redisClient.expire(LuaScriptEnum.DEDUCT_STOCK_LUA.getKey(), 7, TimeUnit.DAYS);
}
Object result = redisClient.evalsha(sha, keys, args, false);
if (result != null) {
return Long.valueOf(String.valueOf(result));
}
log.error("扣减库存返回值为空");
return 0L;
}