redis秒杀简单实现
redis两个key,一个key采用string 记录库存,另一个key使用list记录秒杀成功的用户
基本流程:
1、一个请求带着用户信息过来
2、获取库存
3、判断库存是否>0
4、大于并判断该用户是否已经存在,不存在则保存用户信息,存在则返回已经成功秒杀。
5、否则秒杀失败
代码实现一:
@GetMapping("/seckill")
public String seckill(String name) {
log.info(name + "来了");
String product_number = redisTemplate.opsForValue().get("product_number");
int num = Integer.parseInt(product_number);
if (num <= 0) {
return "商品已被抢光";
}
if (redisTemplate.opsForSet().isMember("product_list", name)) {
return "已经抢过了";
}
redisTemplate.opsForValue().decrement("product_number");
redisTemplate.opsForSet().add("product_list", name);
return "成功";
}
上述流程会出现超卖的情况,必须保证整个查询库存,减少库存,设置用户信息的原子性,才不或出现上述情况
采用lua脚本保证:
代码实现二:
@GetMapping("/seckill2")
public String seckill2(String name) {
log.info(name + "来了");
List<String> keys = Arrays.asList(name);
Long execute = redisTemplate.execute(getRedisScript, keys);
if (execute == 1) {
log.info("抢成功");
return "成功";
} else if (execute == 2) {
log.info("已经抢到了");
return "已经抢过了";
} else if (execute == 0) {
log.info("已经抢光了");
return "抢光了";
}
return "成功";
}
@PostConstruct
public DefaultRedisScript<Long> getscript() {
getRedisScript = new DefaultRedisScript<Long>();
getRedisScript.setResultType(Long.class);
getRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/product.lua")));
return getRedisScript;
}
lua脚本:
local name=KEYS[1];
local userExists=redis.call("sismember","product_list",name);
if tonumber(userExists)==1
then
return 2;
end
local num =redis.call("get","product_number")
if tonumber(num)<=0
then
return 0;
end
redis.call("decr","product_number")
redis.call("sadd","product_list",name)
return 1;