sql判断时间大于0点_Java秒杀系统实战系列-数据库级别Sql的优化与代码的调整

d199d80a7ba949c05e6e623a9d64ee30.gif

本文是“Java秒杀系统实战系列文章”的第十三篇,从本篇文章开始我们将进入“秒杀代码优化”环节,本文将首先从数据库级别Sql的优化入手,结合调整秒杀相关的部分核心代码,实现初步的优化!

上篇文章《Meter压力测试重现秒杀场景中超卖等问题》我们暴露出了“秒杀接口”在面对高并发请求的场景下所出现的“超卖”、“重复秒杀”等问题,并对相应的问题进行了分析,然后就没有然后了……(事了拂衣去!)

问题既然落在我们的手里,那么身为一名程序猿,那是没有理由回避的。通过分析该“秒杀接口”的核心代码,可以发现在数据库层面,其涉及的Sql我们还是可以动一动手脚的!其调整后的“秒杀核心业务逻辑”的完整源代码如下所示:

//商品秒杀核心业务逻辑的处理-mysql的优化@Overridepublic Boolean killItemV2(Integer killId, Integer userId) throws Exception { Boolean result=false; //TODO:判断当前用户是否已经抢购过当前商品 if (itemKillSuccessMapper.countByKillUserId(killId,userId) <= 0){ //A 查询待秒杀商品详情 ItemKill itemKill=itemKillMapper.selectByIdV2(killId); //TODO:判断是否可以被秒杀canKill=1? if (itemKill!=null && 1==itemKill.getCanKill() && itemKill.getTotal()>0){ //B 扣减库存-减一 int res=itemKillMapper.updateKillItemV2(killId); //TODO:扣减是否成功?是-生成秒杀成功的订单,同时通知用户秒杀成功的消息 if (res>0){ commonRecordKillSuccessInfo(itemKill,userId); result=true; } } }else{ throw new Exception("您已经抢购过该商品了!"); } return result;}

△向右滑动查看

首先是对于 注释A 那里的调整,即在获取“秒杀商品详情”时,我们限定了“可秒杀商品的数量total需要大于0”,其对应的代码为:itemKillMapper.updateKillItemV2(killId);完整的动态Sql如下所示:

 SELECT a.*, b.name AS itemName, (CASE WHEN (now() BETWEEN a.start_time AND a.end_time) THEN 1 ELSE 0 END) AS canKill FROM item_kill AS a LEFT JOIN item AS b ON b.id = a.item_id WHERE a.is_active = 1 AND a.id =#{id} AND a.total>0

△向右滑动查看

然后是 注释B 对应的优化调整,即在扣减库存时,我们除了可以保证正常减1的操作之外,还需要保证扣减完之后的数量大于0,即只有在保证扣减完之后的数量大于0之下,该Sql操作后受影响的行数为1,对应的代码为:itemKillMapper.updateKillItemV2(killId);其对应的动态Sql如下所示:

 UPDATE item_kill SET total = total - 1 WHERE id = #{killId} AND total>0

至此,我们在秒杀核心业务逻辑的优化层面~数据库级别Sql的优化 已经搞完了!除此之外,我们还在代码层面进行优化,如下所示:

private void commonRecordKillSuccessInfo(ItemKill kill, Integer userId) throws Exception{ //TODO:记录抢购成功后生成的秒杀订单记录 ItemKillSuccess entity=new ItemKillSuccess(); String orderNo=String.valueOf(snowFlake.nextId()); //entity.setCode(RandomUtil.generateOrderCode()); //传统时间戳+N位随机数 entity.setCode(orderNo); //雪花算法 entity.setItemId(kill.getItemId()); entity.setKillId(kill.getId()); entity.setUserId(userId.toString()); entity.setStatus(SysConstant.OrderStatus.SuccessNotPayed.getCode().byteValue()); entity.setCreateTime(DateTime.now().toDate()); //TODO:学以致用,举一反三 -> 仿照单例模式的双重检验锁写法 if (itemKillSuccessMapper.countByKillUserId(kill.getId(),userId) <= 0){ int res=itemKillSuccessMapper.insertSelective(entity); if (res>0){ //TODO:进行异步邮件消息的通知=rabbitmq+mail rabbitSenderService.sendKillSuccessEmailMsg(orderNo); //TODO:入死信队列,用于 “失效” 超过指定的TTL时间时仍然未支付的订单 rabbitSenderService.sendKillSuccessOrderExpireMsg(orderNo); } }}

△向右滑动查看

即我们在“用户秒杀成功生成订单记录”的代码加入了类似于“单例模式”中的“双重检验锁”,即在生成订单记录,再次判断一下“当前用户是否已经真的没有抢购过该商品”!

在后面的篇章中,我们将开始搬上“中间件”这一利器,并结合本文所介绍Sql的优化和调整后的代码,彻底解决高并发压力测试的场景下出现的“库存超卖”、“重复秒杀”等乱七八糟的问题!

相关视频教程可私信咨询。

推荐阅读:

Java商城秒杀系统的设计与实战教程(SpringBoot版)

Java秒杀系统实战系列-构建SpringBoot多模块项目

Java秒杀系统实战系列-商品秒杀代码实战

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值