redis java实现秒杀

1 设置秒杀商品

提供一个接口给前端,当有秒杀商品上架的时候,完善好商品的信息之后,设置为秒杀商品,设置秒杀开始时间,通过DelayQueuq延迟队列,定时上架,可以秒杀,在redis中set一个key,key值为商品id+Flag,例如‘10000Flag’,值为0,表示未开始秒杀,防止被用户恶意提前开始抢购。

redis代码

redisTemplate.opsForValue().set("10000Flag","0");

redis中再放置一个key/list 数据,key为商品id,list中的值为商品id,list的长度为商品设置的秒杀数量,每一个list中的值称为令牌,也就是秒杀的门票。

int goodsNum = 10;
for(int i = 0; i < goodsNum; i++){                
  redisTemplate.opsForList().leftPush("10000","10000");
}

2 秒杀接口

先要判断是否可以开始秒杀了,一个用户只能秒杀一次,所以要判断用户是否已经秒杀过,通过redis给用户设置一把分布式锁,用setIfAbesent设置,key为商品id+用户id,值为当前时间戳,还未生成订单,1为生成订单,2为已完成,生成订单后,这把锁的获取时间就没有用了。

//2.0操作
String flag = redisTemplate.opsForValue().get("10000");
if("0".equals(flag)){
  //还不可以开始秒杀
  return;
}
//2.1操作
Boolean lockFlag = redisTemplate.opsForValue().setIfAbsent("goodId"+"userId",        System.currentTimeMillis()+"",600, TimeUnit.SECONDS);
if(lockFlag){
    //获得锁 可以继续 判断商品数量
}

当用户拿到锁了,再判断秒杀商品的数量

//2.2操作
String good = redisTemplate.opsForList().leftPop("10000");
if(good != null){
   //说明还有商品 可以秒杀 否则返回秒杀商品已售完
   //2.3 生成订单 订单中包含商品id和用户id 去支付
   //订单生成完毕
   redisTemplate.opsForValue().set("商品id"+"用户id","1");
}

3 用户取消支付

定时任务判断用户支付状态,当用户超过支付截止时间+30s(30s为了预防网络延迟因素)未支付,则设置状态为取消,此时通过订单记录里的商品id和用户id,删除掉用户的锁

redisTemplate.delete("商品id"+"用户id");

并且将取得的list中的令牌还回去。其他用户可以继续秒杀。

redisTemplate.opsForList().leftPush("商品id","商品id");

4 支付完成

redisTemplate.opsForValue().set("商品id"+"用户id","2");

5 清理redis数据

定时任务去清理,秒杀第二天0点的时候去批量删除数据

//删除商品是否开始秒杀标签
redisTemplate.delete("商品id"+"Flag");
//删除商品数量
redisTemplate.delete("商品id");

6 问题

当程序在2.3用户创建订单之前,获取锁之后挂了,这时候这个用户又进来了,但是锁已经存在了,怎么处理,还得保证上一次用户获取这个锁的时间间隔不能太短。

//2.1操作
Boolean lockFlag = redisTemplate.opsForValue().setIfAbsent("goodId"+"userId","0",600, TimeUnit.SECONDS);
if(!lockFlag){
   //没获得锁
   String timeValue = redisTemplate.opsForValue().get("goodId"+"userId");
   // timeValue.length() 说明不是1和2 还未生成订单
   if(timeValue.length() > 1){
         Long oldTime = Long.parseLong(timeValue);
         if(oldTime - System.currentTimeMillis() < 5000){
             //距离第一次生成分布式锁过去不足5s返回系统繁忙,请等待
             return;
         }
   }
   //根据商品id+用户id 查询订单
   //如果存在 直接返回
   if(timeValue.length() > 1 || hasOrder){
        return;
   }
   //不存在 加锁 加在类上
   synchronized(Demo.class){
         //再判断是否有订单 有直接返回 没有继续。
         if(timeValue.length() > 1 || hasOrder){
            return;
         }
   }
}
//如果没有提前结束 就表明这个用户没有订单,用户经过登陆验证,认为是可靠的,这个分布式锁是可重入的,可以继续由这个用户使用。

如果2.2操作 即获取商品令牌之后,生成订单之前挂了,用户再次进来,不应该再去减库存,应该直接去生成订单的操作,所以当商品令牌扣减之后,讲用户和令牌绑定在一起。

//2.2操作
String good = redisTemplate.opsForValue().get("goodId"+"userId");
//是否需要拿商品令牌
Boolean flag = true;
if( "商品id".equals(good)){
   //说明还未生成订单,但是商品的令牌已经拿到了,根据用户id和商品id去查询
   if(hasOrder){
      return;
   }
   synchronized(Demo.class){
     if(hasOrder || good.length == 1){
      return;
     }else{
       flag = false;
     }
   }
}
if(flag){
    good = redisTemplate.opsForList().leftPop("10000");
    if(good == null){
       //秒杀商品已售完。
       return;
    }
}
//说明还有商品 可以秒杀 否则返回秒杀商品已售完
redisTemplate.opsForValue().set("商品id"+"用户id", good);
//2.3 生成订单 订单中包含商品id和用户id 去支付
//订单生成完毕
redisTemplate.opsForValue().set("商品id"+"用户id","1");

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值