高并发秒杀系统分析

本人属于个人总结: 

原文转载: https://blog.csdn.net/jeffleo/article/details/56015710

名词解释:

    QPS 每秒查询率QPS

 

本文是学习了immoc网视频之后的个人理解和知识汇总 
项目源码:https://github.com/jeff-leo/SpikeSystem,希望大家能starfork

   

1. 秒杀优化四个方面

(1) 详情页面

使用CDN加速,将cssjshtml 不变的数据存放到cdn服务器;

 

用户请求较近的cdn缓存服务器,获取请求数据,如果cdn没有,则cdn会自动向上直至找到网站根源;

使用CDN服务的网站,只需将其域名解析权交给CDNGSLB设备,将需要分发的内容注入CDN,就可以实现内容加速了。

(2) 获取系统时间操作的优化

Java每秒访问内存10亿次,服务器获取系统时间不用优化;

3. 秒杀地址接口获取的优化

 

先操作redis,每个用户请求在redis中进行记录,如果当前请求量增加,修改redis记录,暂时关闭秒杀接口;

 

 

(3)  Redis是否可以管理库存?

库存使用数据库维护比较好,秒杀数量则可以利用redis的原子性进行控制;

凡是需要进行写操作的数据都不适合做缓存。 库存不能在redis中维护;

(4) 秒杀操作高并发的问题(重点)

不能在redis中作库存管理,因为会导致数据一致性问题,(凡是需要进行写操作的数据都不适合做缓存。)

① 秒杀流程:

 

用户请求-> 已提交请耐心等待

② 引发问题:

热点商品同时秒杀,会引起数据库行级锁,这是其他用户等待;

③ 解决办法:

1) 方案一介绍:

利用redis原子计数器,秒杀商品计数器-1,0则隐藏接口,不再秒杀;

 

然后记录用户,将用户ID与商品ID,购买数量发送MQ中,进行订单处理,此时返回用户“正在秒杀,请等待。。。”

接着消息队列的消费者进程异步获取数据,生成订单信息,此时订单状态为“未付款”

页面显示“正在秒杀,请等待。。。” ,但页面每2秒进行一次异步查询秒杀结果,根据结果进行页面跳转处理;

秒杀成功后,30分钟内进行付款;过期则将商品数量返还给秒杀原子计数器;

用户付款成功,真正修改数据库库存状态;

 

2) 方案一问题:

需要强大的运维团队,NoSql不如MySql稳定

重发秒杀的问题。一个用户重发秒杀请求,避免这种情况需要创建另一个NoSql存储请求者信息,如果发现存在该用户秒杀请求,下一次不进行处理;

 

问题:mysql性能上不去,是因为存在网络延迟

把服务器的执行逻辑放在mysql服务端,避免网络延迟和gc延迟

 

 

 

3) 方案二介绍:

使用存储过程完成MySql秒杀的事物操作;

 

<!-- mybatis 调用存储过程-->
    <select id="seckillByProduce" statementType="CALLABLE">
        CALL excuteSeckill(
          #{ seckillId , jdbcType = BIGINT , mode= IN },
          #{ phone ,jdbcType = BIGINT , mode= IN },
          #{ killTime , jdbcType = TIMESTAMP , mode= IN },
          #{ result , jdbcType = BIGINT , mode= OUT }
        )
    </select>

 

 public SeckillExecution executeSeckillByProducure(long seckillId, long userPhone, String md5) {
        if(md5 == null || !md5.equals(getMd5(seckillId))){//数据篡改
            return new SeckillExecution(seckillId, SeckillStatEnum.DATE_REWRITE);
        }
        Date now = new Date();
        Map params = new HashMap();
        params.put("seckillId", seckillId);
        params.put("phone", userPhone);
        params.put("killTime", now);
        params.put("result", null);
        try {
            seckillMapper.seckillByProduce(params);
            Integer result = MapUtils.getInteger(params, "result", -3);
            if(result == 1){//成功
                SuccessKilled successKilled = successKilledMapper.queryByIdWithSeckill(seckillId, userPhone);
                return new SeckillExecution(seckillId, SeckillStatEnum.SUCCESS, successKilled);
            }else{//如果失败,把错误状态-1或者-2等返回
                return new SeckillExecution(seckillId, SeckillStatEnum.stateOf(result));
            }
        } catch (Exception e){//出现异常也要返回
            logger.error(e.getMessage(), e);
            return new SeckillExecution(seckillId, SeckillStatEnum.INNER_ERROR);
        }
    }

 

参考: 
CDN的基本工作过程 
imooc

更多关于系统架构和分布式的了解: 
分布式架构的演进

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值