文档章节

Java秒杀系统实战系列~数据库级别Sql的优化与代码的调整

稳杰
 稳杰
发布于 2019/08/12 10:36
字数 1160
阅读 116
收藏 0

摘要:

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

内容:

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

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

//商品秒杀核心业务逻辑的处理-mysql的优化
@Override
public 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如下所示:

<!--获取秒杀详情V2-->
<select id="selectByIdV2" resultType="com.debug.kill.model.entity.ItemKill">
  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
</select>

 

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

<!--抢购商品,剩余数量减一-->
<update id="updateKillItemV2">
  UPDATE item_kill
  SET total = total - 1
  WHERE id = #{killId} AND total>0
</update>

至此,我们在秒杀核心业务逻辑的优化层面~数据库级别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的优化和调整后的代码,彻底解决高并发压力测试的场景下出现的“库存超卖”、“重复秒杀”等乱七八糟的问题!

补充:

1、目前,这一秒杀系统的整体构建与代码实战已经全部完成了,完整的源代码数据库地址可以来这里下载:https://gitee.com/steadyjack/SpringBoot-SecondKill 记得Fork跟Star啊!!

2、最后,不要忘记了关注一下Debug的技术微信公众号:

© 著作权归作者所有

稳杰
粉丝 4
博文 18
码字总数 31856
作品 0
广州
高级程序员
私信 提问
加载中

评论(0)

三流程序员与一流程序员之间的区别,看看你是属于哪一类?

源码系列 手写spring mvc框架 基于Spring JDBC手写ORM框架 实现自己的MyBatis Spring AOP实战之源码分析 Spring IOC高级特性应用分析 ORM框架底层实现原理剖析 手写Spring MVC框架实现 手把手...

茶轴的青春
2018/04/17
34
0
阿里面试问题总结(Java工程师)

写在前面 今天是我来杭州找工作后参加的第一次现场面试,也算是工作以来第一次正式的技术面试,超级超级紧张!两个面试官貌似都是做中间件和数据库编程方向的,问的很多我都不太擅长,两轮下...

别打我会飞
2019/03/04
112
0
京东4面(Java研发):事务隔离+乐观锁+HashMap+秒杀设计+微服务

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/t4i2b10X4c22nF6A/article/details/84347063 一面(基础面:约五十分钟) 自我介绍,主要讲讲做了什么和擅长什...

JAVA高级架构v
2018/11/22
0
0
Java开发者不会这些永远都只能是三流程序员,细数一下你是不是?

源码系列 手写spring mvc框架 基于Spring JDBC手写ORM框架 实现自己的MyBatis Spring AOP实战之源码分析 Spring IOC高级特性应用分析 ORM框架底层实现原理剖析 手写Spring MVC框架实现 手把手...

美的让人心动
2018/04/16
122
5
同样是搞Java,年薪15W和50W的到底差在哪里?

  在这个IT系统动辄就是上亿流量的时代,Java作为大数据时代应用最广泛的语言,诞生了一批又一批的新技术,包括HBase、Hadoop、MQ、Netty、SpringCloud等等 。   一些独角兽公司以及腾讯...

java进阶架构师
2018/11/07
0
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周三乱弹 —— 提高不了工作效率和脸有关系

Osc乱弹歌单(2020)请戳(这里) 【今日歌曲】 @薛定谔的兄弟 :分享洛神有语创建的歌单「我喜欢的音乐」: 1 《夏令时记录(piano.ver)》- ゆめこ 手机党少年们想听歌,请使劲儿戳(这里) ...

小小编辑
今天
67
2
List的一波操作

public static void main(String[] args) { List<Entity> list = new ArrayList<>(); list.add(new Entity(1)); list.add(new Entity(2)); list.add(new Entity(3)); ......

那个猩猩很亮
今天
75
0
Spring基础

主要用于service层; 轻量级java开发框架; 各层 web层:struts,spring-MVC service层:spring dao层:hibernate,mybatis , jdbcTemplate --> spring-data Spring核心:控制反转IOC 切面编...

七宝1
今天
30
0
解决overflow+border-radius+transform圆角问题

网上还有其他版本,但是对我来说都不好使,下面是我在Chrome上的代码。overflow:hidden依然是不能正常使用,换成unset就可以,读者如果有更好的解决方案,请留言,谢谢。 <figure> <img...

hi懒喵
今天
53
0
《C语言》—— 数组

书籍使我变成了一个幸福的人,使我的生活变成轻松而舒适的诗。——高尔基 本文已经收录至我的GitHub,欢迎大家踊跃star 和 issues。 https://github.com/midou-tech/articles 点关注,不迷路!...

龙跃十二
今天
84
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部