前言:
👏作者简介:我是笑霸final,一名热爱技术的在校学生。
📝个人主页:个人主页1 || 笑霸final的主页2
📕系列专栏:《项目专栏》
📧如果文章知识点有错误的地方,请指正!和大家一起学习,一起进步👀
🔥如果感觉博主的文章还不错的话,👍点赞👍 + 👀关注👀 + 🤏收藏🤏
目录
🐉介绍🐉
🐉全局唯一ID🐉
我们使用全局id生成器
我们能不能用redis来完成这个任务?
代码
// 1. 生成时间戳//2.生成序列号//3.拼接并返回
获取时间戳
publicstaticvoidmain(String[] args){//获取2022年 1月 1号 0点的秒数LocalDateTime time =LocalDateTime.of(2022,1,1,0,0);long second = time.toEpochSecond(ZoneOffset.UTC);
log.info(Long.toString(second));}
publicLongnexId(String keyPrefix){// 1. 生成时间戳 31位数字 单位秒LocalDateTime now =LocalDateTime.now();long nowSecond = now.toEpochSecond(ZoneOffset.UTC);//当前秒数Long timestamp=nowSecond-BEG_TIMESTAMP;//2.生成序列号//2.1获取当前日期String yyyyMMdd = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));//2.2自增长Long count = stringRedisTemplate.opsForValue().increment("icr:"+ keyPrefix +":"+ yyyyMMdd);//3.拼接并返回return timestamp<<32| count;}
🐉添加优惠卷🐉
背景:每个商代都可以发布优惠卷,评价卷都可以任意购买,优惠卷需要秒杀抢购!!
数据库
- 平价卷
- 秒杀卷
🐉新增优惠卷json数据
url地址:http://localhost:8081/voucher/seckill
{"shopId":1,"title":"100元代金卷","subTitle":"周一到周五可用","rules":"测试","payValue":8000,"actualValue":10000,"type":1,"stock":100,"beginTime":"2022-01-25T10:09:17","endTime":"2023-01-25T10:09:17",}
🐉实现优惠卷秒杀下单(基本下单)🐉
一般流程
- 判断秒杀是否开始,或者结束
- 库存是否充足
代码
@ServicepublicclassVoucherOrderServiceImplextendsServiceImpl<VoucherOrderMapper,VoucherOrder>implementsIVoucherOrderService{@ResourceprivateISeckillVoucherService seckillVoucherService;@ResourceprivateRedisIdWorker redisIdWorker;@Override@Transactional//两张表 加上事务publicResultseckillVoucher(Long voucherId){//1.查询idSeckillVoucher voucher = seckillVoucherService.getById(voucherId);//2.判断是否开始if(voucher.getBeginTime().isAfter(LocalDateTime.now())){//没有开始returnResult.fail("时间还没开始");}//3.判断是否结束if(voucher.getEndTime().isBefore(LocalDateTime.now())){//已经结束returnResult.fail("已经结束");}//4.判断库存是否充足if(voucher.getStock()<1){//库存不足returnResult.fail("库存不足");}//5.扣减库存boolean success= seckillVoucherService
.update().setSql("stock=stock-1").eq("voucher_id",voucherId).update();if(!success){//库存不足returnResult.fail("库存不足");}//6.创建订单VoucherOrder voucherOrder =newVoucherOrder();//6.1订单id 用id全局唯一生成器Long orderId = redisIdWorker.nexId("order");
voucherOrder.setId(orderId);//6.2用户idLong userId =UserHolder.getUser().getId();
voucherOrder.setUserId(userId);//6.3代金卷id
voucherOrder.setVoucherId(voucherId);save(voucherOrder);//7.返回idreturnResult.ok(orderId);}}
🐉实现优惠卷秒杀下单(超卖问题)🐉
解决超卖问题可以加锁:
- 乐观锁:认为线程安全问题不一定会发生,因此不加锁,只是在更新数据时去判断有没有其它线程对数据做了修改。如果没有修改则认为是安全的,自己才更新数据。如果已经被其它线程修改说明发生了安全问题,此时可以重试或异常。
- 悲观锁:认为线程安全问题一定会发生,因此在操作数据之前先获取锁,确保线程串行执行。例如Synchronized、Lock都属于悲观锁
乐观锁实现方案
1.版本号法(运用最广泛的):每次更新数据库的时候按照版本查询,并且要更新版本。
2.CAS
CAS是英文单词Compare And Swap的缩写,翻译过来就是比较并替换。
CAS机制当中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B。
更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B。
CAS法代码:
@ServicepublicclassVoucherOrderServiceImplextendsServiceImpl<VoucherOrderMapper,VoucherOrder>implementsIVoucherOrderService{@ResourceprivateISeckillVoucherService seckillVoucherService;@ResourceprivateRedisIdWorker redisIdWorker;@Override@Transactional//两张表 加上事务publicResultseckillVoucher(Long voucherId){//1.查询idSeckillVoucher voucher = seckillVoucherService.getById(voucherId);//2.判断是否开始if(voucher.getBeginTime().isAfter(LocalDateTime.now())){//没有开始returnResult.fail("时间还没开始");}//3.判断是否结束if(voucher.getEndTime().isBefore(LocalDateTime.now())){//已经结束returnResult.fail("已经结束");}//4.判断库存是否充足if(voucher.getStock()<1){//库存不足returnResult.fail("库存不足");}//5.扣减库存 乐观锁 在这一刻去判断更新时和查询到的库存是否一致// boolean success= seckillVoucherService// .update()// .setSql("stock=stock-1")// .eq("voucher_id",voucherId).update();boolean success= seckillVoucherService
.update().setSql("stock=stock-1").eq("voucher_id",voucherId).eq("stock",voucher.getStock()).update();if(!success){//库存不足returnResult.fail("库存不足");}//6.创建订单VoucherOrder voucherOrder =newVoucherOrder();//6.1订单id 用id全局唯一生成器Long orderId = redisIdWorker.nexId("order");
voucherOrder.setId(orderId);//6.2用户idLong userId =UserHolder.getUser().getId();
voucherOrder.setUserId(userId);//6.3代金卷id
voucherOrder.setVoucherId(voucherId);save(voucherOrder);//7.返回idreturnResult.ok(orderId);}}
弊端:成功率太低:
改进一下
判断库存和0的大小
🐉实现优惠卷秒杀下单(一人一单)🐉
@ServicepublicclassVoucherOrderServiceImplextendsServiceImpl<VoucherOrderMapper,VoucherOrder>implementsIVoucherOrderService{@ResourceprivateISeckillVoucherService seckillVoucherService;@ResourceprivateRedisIdWorker redisIdWorker;@OverridepublicResultseckillVoucher(Long voucherId){//1.查询idSeckillVoucher voucher = seckillVoucherService.getById(voucherId);//2.判断是否开始if(voucher.getBeginTime().isAfter(LocalDateTime.now())){//没有开始returnResult.fail("时间还没开始");}//3.判断是否结束if(voucher.getEndTime().isBefore(LocalDateTime.now())){//已经结束returnResult.fail("已经结束");}//4.判断库存是否充足if(voucher.getStock()<1){//库存不足returnResult.fail("库存不足");}Long userId =UserHolder.getUser().getId();//intern()在常量池里先去找一样的地址返回synchronized(userId.toString().intern()){VoucherOrderServiceImpl proxy
=(VoucherOrderServiceImpl)AopContext.currentProxy();//代理对象//因为没有加事务 事务用的代理对象 可能存在事务失效//没有用代理对象默认是 this.creteVoucherOrder(voucherId)return proxy.creteVoucherOrder(voucherId);}}@Transactional//两张表 加上事务publicResultcreteVoucherOrder(Long voucherId){//5一人一单Long userId =UserHolder.getUser().getId();//5.1查询订单Integer count =query().eq("user_id", userId).eq("voucher_id", voucherId).count();//5.2查询是否存在if(count >0){//存在就不能下单了returnResult.fail("你已经购买过了,只能买一次哟");}//6.扣减库存 乐观锁 在这一刻去判断更新时和查询到的库存是否一致// boolean success= seckillVoucherService// .update()// .setSql("stock=stock-1")// .eq("voucher_id",voucherId).update();boolean success = seckillVoucherService
.update().setSql("stock=stock-1").eq("voucher_id", voucherId)// .eq("stock",voucher.getStock()).gt("stock",0)//判断 stock>0可行.update();if(!success){//库存不足returnResult.fail("库存不足");}//7.创建订单VoucherOrder voucherOrder =newVoucherOrder();//7.1订单id 用id全局唯一生成器Long orderId = redisIdWorker.nexId("order");
voucherOrder.setId(orderId);//7.2用户id
voucherOrder.setUserId(userId);//7.3代金卷id
voucherOrder.setVoucherId(voucherId);save(voucherOrder);//8.返回idreturnResult.ok(orderId);}}
需要加上
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId></dependency>
同时启动类也加上
@EnableAspectJAutoProxy(exposeProxy = true)//暴露代理对象
版权归原作者 笑霸final 所有, 如有侵权,请联系我们删除。