在高并发的情况下,利用redis来处理库存超卖和遗留问题
首先现在redis中放上商品的库存数量为100间商品,在初始化一个set集合用于放秒杀成功的用户id,本用例先放进去一个id=10000的用户。
@RequestMapping("skill")
@ResponseBody
public String kill(HttpServletRequest request){
String userId=new Random().nextInt(50000)+"";
String productId="0101";
boolean b=doSkill(productId,userId);
return "";
}
private boolean doSkill(String productId, String userId) {
Jedis jedis = redisUtil.getJedis();
String kcKey="Seckill:"+productId+":kc";
String userKey="Seckill:"+productId+":user";
String s = jedis.get(kcKey);
if (null==s){
System.out.println("秒杀还么开始");
jedis.close();
return false;
}
//秒杀已经成功不能再次秒杀
if (jedis.sismember(userKey,userId))
{
System.out.println("秒杀已经成功不能再次秒杀");
jedis.close();
return false;
}
if (Integer.parseInt(s)<=0)
{
System.out.println("秒杀结束");
jedis.close();
return false;
}
//减库存,家人
jedis.decr(kcKey);
jedis.sadd(userKey,userId);
jedis.close();
return true;
}
下面进行压力测试(使用ab工具,具体可百度,不在此做详细说明):
从上图结果看明显超卖了。
下面解决超卖:主要加事务,更改两处代码
下图显示redis的数据明显没有超卖,但是会有库存遗留,本次数据量和并发量小才没有。
解决库存遗留:lua脚本解决
把库存容量加到200,再次请求结果如下图,明显出现了库存遗留
简单介绍下lua脚本:它类似于redis的事务具有一定的原子性,不会被其他命令插队,可以完成一些redis的事务的工作。
注意:redis的版本在2.6及以上的版本
private void doSkillByScript(String uid,String prodid){
Jedis jedis = redisUtil.getJedis();
String secKillScript ="local userid=KEYS[1];\r\n" +
"local prodid=KEYS[2];\r\n" +
"local qtkey='Seckill:'..prodid..\":kc\";\r\n" +
"local usersKey='Seckill:'..prodid..\":user\";\r\n" +
"local userExists=redis.call(\"sismember\",usersKey,userid);\r\n" +
"if tonumber(userExists)==1 then \r\n" +
" return 2;\r\n" +
"end\r\n" +
"local num= redis.call(\"get\" ,qtkey);\r\n" +
"if tonumber(num)<=0 then \r\n" +
" return 0;\r\n" +
"else \r\n" +
" redis.call(\"decr\",qtkey);\r\n" +
" redis.call(\"sadd\",usersKey,userid);\r\n" +
"end\r\n" +
"return 1" ;
/* String sha1= jedis.scriptLoad(secKillScript);
Object result= jedis.evalsha(sha1, 2, uid,prodid);*/
Object result = jedis.eval(secKillScript, 2, uid, prodid);
String reString=String.valueOf(result);
if ("0".equals( reString ) ) {
System.err.println("已抢空!!");
}else if("1".equals( reString ) ) {
System.out.println("抢购成功!!!!");
}else if("2".equals( reString ) ) {
System.err.println("该用户已抢过!!");
}else{
System.err.println("抢购异常!!");
}
jedis.close();
}