秒杀案例设计

存入redis中的设计

商品库存
key:  sk:prodid:qt
string: 剩余个数

秒杀成功者清单
key: sk:prodid:user
set: 成功者的user_id 成功者的user_id 成功者的user_id

代码实现

public static boolean doSecKill(String uid,String prodid) throws IOException{
  //拼接向redis中保存库存和秒杀成功人员名单的key
  String kcKey = "sk:" + prodid + ":qt";
  String userKey = "sk:" + prodid + ":user";
  //创建Jredis连接池对象
  JedisPool jedisPoolInstance = JedisPoolUtil.getJedisPoolInstance();
  Jedis jedis = jedisPoolInstance.getResource();

  //添加乐观锁
  jedis.watch(kcKey);

  //获取库存
  String count = jedis.get(kcKey);
  //判断库存的数量
  if("0".equals(count)){
    //证明已经秒完
    System.err.println("已经秒完");
    JedisPoolUtil.release(jedisPoolInstance.jedis);
    return false;
  }

  //添加事务
  Transaction multi = jedis.multi();  

  //有库存的情况下需要进行以下操作
  //将库存减1
  multi.decr(kcKey);
  //保存秒杀成功者的名单
  multi.sadd(userKey, uid);
  //执行
  List<Object> exec = multi.exec();
  if(exec == null || exec.size() == 0){
    System.err.println("秒杀失败");
    JedisPoolUtil.release(jedisPoolInstance.jedis);
    return false;
  }

  System.out.println("秒杀成功!!");
  return true;
}

利用lua脚本解决超卖和库存遗留问题

local userid=KEYS[1];
local userid=KEYS[2];
local qtkey="sk:"..prodid..":qt";
local usersKey="sk:"..prodid..":user";
local userExists=redis.call("sismember",usersKey,userid);
if tonumber(userExists)==1 then
  return 2;
end
local num=redis.call("get", qtkey);
if tonumber(num) <=0 then
  return 0;
else
  redis.call("decr", qtkey);
  redis.call("sadd",usersKey,userid);
end
return 1;

代码实现

public static boolean doSecKill(String uid,String prodid) throws IOException{
  JedisPool jedispool = JedisPoolUtil.getJedisPoolInstance();
  Jedis jedis = jedispool.getResource();

  String sha1 = jedis.scriptLoad(secKillScript);
  Object result = jedis.evalsha(sha1, 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();
  return true;
}

# secKillScript
staic String secKillScript = "local userid=KEYS[1];\r\n" +
                            "local userid=KEYS[2];\r\n" +
                            "local qtkey='sk:'..prodid..\":qt\";\r\n" +
                            "local usersKey='sk:'..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;"
posted @ 2023-07-09 09:50  我在路上回头看  阅读(10)  评论(0编辑  收藏  举报