lua脚本
-- 1.参数列表
-- 1.1.优惠券id
local voucherId = ARGV[1]
-- 1.2.用户id
local userId = ARGV[2]
-- 1.3.订单id
local orderId = ARGV[3]
-- 2.数据key
-- 2.1.库存key
local stockKey = 'seckill:stock:' .. voucherId
-- 2.2.订单key
local orderKey = 'seckill:order:' .. voucherId
-- 3.脚本业务
-- 3.1.判断库存是否充足 get stockKey
if(tonumber(redis.call('get', stockKey)) <= 0) then
-- 3.2.库存不足,返回1
return 1
end
-- 3.2.判断用户是否下单 SISMEMBER orderKey userId
if(redis.call('sismember', orderKey, userId) == 1) then
-- 3.3.存在,说明是重复下单,返回2
return 2
end
-- 3.4.扣库存 incrby stockKey -1
redis.call('incrby', stockKey, -1)
-- 3.5.下单(保存用户)sadd orderKey userId
redis.call('sadd', orderKey, userId)
-- 消息队列一般用MQ
-- 3.6.发送消息到队列中, XADD stream.orders * k1 v1 k2 v2 ...
-- redis.call('xadd', 'stream.orders', '*', 'userId', userId, 'voucherId', voucherId, 'id', orderId)
return 0
将lua脚本放置在resources下
redisUtil,lua脚本要使用stringRedisTemplate
/**
* 执行lua脚本返回结果 stringRedisTemplate
*
* @param defaultRedisScript 脚本信息
* @param prizeId 商品id
* @param userId 用户id
* @return 0=有购买资格,1=库存不足,2=不能重复下单
*/
public Long seckillLua(DefaultRedisScript<Long> defaultRedisScript, Integer prizeId, Integer userId) {
Long result = stringRedisTemplate.execute(
defaultRedisScript,
Collections.emptyList(),
prizeId.toString(),
userId.toString()
);
return result;
}
下单Demo
private static final DefaultRedisScript<Long> SECKILL_SCRIPT;
static {
SECKILL_SCRIPT = new DefaultRedisScript<>();
SECKILL_SCRIPT.setLocation(new ClassPathResource("seckill.lua"));
SECKILL_SCRIPT.setResultType(Long.class);
}
@ApiOperation("lua秒杀测试")
@GetMapping("lua/test/{prize_id}/{user_id}")
public ApiResponse seckill(@PathVariable("prize_id") Integer prizeId, @PathVariable("user_id") Integer userId) {
//校验秒杀时间
//略
//使用Lua脚本校验个人资格(一人一单)
Long result = redisUtils.seckillLua(SECKILL_SCRIPT, prizeId, userId);
int r = result.intValue();
if (r == 1){
return ApiResponse.success("库存不足");
}
if (r == 2){
return ApiResponse.success("不能重复下单");
}
if (r == 0) {
ApiResponse.success("下单成功");
}
String msg = userId + "_" + prizeId;
//发送Mq进行消息发送
MqUtil.msgSend("TOPIC_USERNAME",msg);
return ApiResponse.success("下单成功");
}