• 秒杀系统设计&测试


    秒杀系统设计

    说起秒杀,从双十一购物到春节抢红包,再到逢年过节抢⻋票,“秒杀”的场景在我们的生活中处处可⻅。简单来说,秒杀就是在同一个时刻有大量的请求,争抢购买同一个商品并完成交易的过程。

    不管校招,还是社招,如何设计一个秒杀系统的面试题经常出现,如果懂得其中原理,就可以对答如流,不过涉及到一些瓶颈优化,有些同学就未必都能答出。

    面试官:简单说一下秒杀系统的设计思路?

    这种题目,小菜是准备过的,巴拉巴拉的说了一堆。

    面试官:那这里是怎么保证秒杀成功的?

    小菜:&8^%#

    面试官:你这里用了Redis,有什么用?

    小菜:#¥&……%

    面试官:你用什么测试过这个系统的并发量?

    小菜:&*%$^&.

    面试官:你觉得你这个系统还可以再优化么?

    小菜:&%……¥)

    面试官:你知道这个系统的瓶颈在哪里吗?如果流量再大10倍,怎么应对?

    小菜:88!

    秒杀系统的整体架构可以概括为“稳、准、快”。

    整个系统架构要满足高可用,流量符合预期时肯定要稳定,超出预期时也同样不能掉链子,你要保证秒杀活动顺利完成,即秒杀商品顺利地卖出去,这个是最基本的前提。

    你的业务需求是秒杀10台iPhone XS,那就只能成交10台,多一台少一台都不行。一旦库存不对,那平台就要承担损失。

    就是说系统的性能要足够高,否则你怎么支撑这么大的流量呢?不光是服务端要做极致的性能优化,而且在整个请求链路上都要做协同的优化,每个地方快一点,整个系统就完美了。

    设计思路:将请求拦截在系统上游,降低下游压力。在一个并发量大,实际需求小的系统中,应当尽量在前端拦截无效流量,降低下游服务器和数据库的压力,不然很可能造成数据库读写锁冲突,甚至导致死锁,最终请求超时。

    限流:前端直接限流,允许少部分流量流向后端。

    削峰:瞬时大流量峰值容易压垮系统,解决这个问题是重中之重。常用的消峰方法有异步处理、缓存和消息中间件等技术。

    异步处理:秒杀系统是一个高并发系统,采用异步处理模式可以极大地提高系统并发量,其实异步处理就是削峰的一种实现方式。

    内存缓存:秒杀系统最大的瓶颈一般都是数据库读写,由于数据库读写属于磁盘IO,性能很低,如果能够把部分数据或业务逻辑转移到内存缓存,效率会有极大地提升。

    消息队列:消息队列可以削峰,将拦截大量并发请求,这也是一个异步处理过程,后台业务根据自己的处理能力,从消息队列中主动的拉取请求消息进行业务处理。

    可拓展:当然如果我们想支持更多用户,更大的并发,最好就将系统设计成弹性可拓展的,如果流量来了,拓展机器就好了,像淘宝、京东等双十一活动时会临时增加大量机器应对交易高峰。

    秒杀系统测试

    对于大并发量的系统,有几个可能需要优化的点,下面我们要一步步测试来优化这个系统。

    测试目标
    对于一个系统,几个常用的评价指标是:平均响应时间、吞吐率、qps等。我的测试主要测试3个接口

    主页(访问根路径,没有数据库交互)
    秒杀接口暴露(暴露秒杀接口,有后台数据交互)
    执行秒杀操作(插入秒杀成功记录和减库存一个完整的事务操作)
    对于这三个接口,我们主要的测试目标和优化目标是平均响应时间,当然这是建立在数据正确返回的基础上的,失败率太高那这个平均响应时间是没有意义的。
    这里的优化侧重于后端数据库和内存方面的优化。

    测试环境
    我是在Windows10下用jmeter来进行负载测试和压力测试,其他环境如下,涉及具体配置再提。

    Tomcat8.0.38
    Jdk1.8 hotspot vm
    Mysql 5.7
    Redis 2.7.3
    测试过程
    首先进行主页测试,我们访问tomcat的主页,使用jmeter的线程组中的线程数模拟用户数,不断增加线程数对主页进行性能测试。
    我们将结果数据写到一个xml文件中。首先我们模拟5000个用户同时请求主页。

    5000个用户同时请求主页
    设置循环次数为2,即一共有10000个请求将被发送。

     

    从响应的结果可以看到,没有错误数,这10000个请求全部返回成功了,只是有的请求慢有的请求快。平均的响应时间在300ms,50%的请求的响应时间平均为87ms。到后面越来越多的请求开始等待,这里可以想到的优化的点在于tomcat的线程池中线程的数量,越来越多的请求在等待队列中。查看tomcat的配置后发现最大线程数为maxThreads=”150”,好那我们用150个线程,循环10次,也就是一共1500个请求,那结果会是什么样呢?

     平均相应时间为5ms,前50%的请求的平均响应时间为1ms。

    但是这里并不能直接修改tomcat的最大线程数来优化。复杂点说就是这是一个复杂的东西,线程数越大,你也要有相应的cpu来执行啊。直接点说就是,我不懂。。。
    我把tomcat的线程数设置为500,然后起5000个线程发送10000个请求,然后得到了:

     

    比之前的更差了。无论是平均相应时间还是错误率。简单粗暴的去改线程数是不可行的。这里我们不去管tomcat的线程数或者是其他层面的优化,我们只专注于后端数据库层面的优化。

    500个用户同时请求暴露秒杀接口
    为什么用500个,是为了减少因为tomcat请求等待带来的数据误差。
    直接向MySQL请求数据
    先模拟500个用户,每个用户发送10次请求。该请求相应的操作为根据id向数据库查询一条记录。得到了这样的数据。 

    期间打开windows的性能监控器,发现磁盘IO有变化,IO百分比最高的时候也不超过15%。
    这样的操作,错误率为0,相当稳定,平均响应时间为1406ms。
    模拟5000个用户,每个用户发送一次请求。、

    磁盘的IO百分比一度达到了100%。从数据的绝对值来看,这样的测试没有意义了,因为瓶颈不在MySQL,而在我们前面分析过的tomcat。但是数据的相对值是有意义的。

    使用redis缓存数据
    还是模拟500个用户,每个用户发送10次请求。

    响应速度显著提高,注意一个值,Min=1,有些请求几乎不足1ms,因为redis直接从内存读取数值,非常快。如果不是tomcat的请求在排队,我想平均响应时间是个位数。
    Redis下模拟5000个用户,每个用户发送一次请求。会是什么结果呢?

     

    使用150个线程,循环100次,即发送15000次请求,得到:

    可以看到,150个用户的话这种响应速度是可以接受的,响应的瓶颈在于tomcat的请求排队等待。

    ———–我是分割线—————-

    这个优化的过程我想到了很多东西,感觉就是,优化是无止尽的。
    比如,我想到了内存回收那一块。选用合适的垃圾收集器,尽可能地减少GC时stop the world的时间和次数显然对于一个秒杀系统来说是非常对的优化方向。这里我尝试用过几款垃圾收集器比如parNew,G1来对比他们的平均响应时间,但是多次测试后没有明显的差距。有两个原因,一是这个接口没有产生太多的大对象,二是这个优化并不太明显。后面有机会的话还是希望继续在内存方面进行优化,感觉内存回收方面有点神秘,很想试一试。

    ————我是分割线————-

    可以看到redis的使用很大程度上提高了响应的时间。上面那个接口只是暴露一个地址,这些地址每个产品都只有一个,那这样的场景是可以用redis的。但是有些操作并没有办法使用缓存。比如执行秒杀这个操作。
    这个操作是个事务型操作。如果其中一个操作失败了,我就让他rollback,这样的话,应该会有更多的并发问题。

    转载自:

    https://blog.csdn.net/weixin_34109408/article/details/89623725

    https://blog.csdn.net/OWEN_7/article/details/78319115

  • 相关阅读:
    JSF大概介绍
    专门用于swing显示的工具类
    oracle 中查某表的所有列字段
    从实例谈OOP、工厂模式和重构
    C#中结构或类的嵌套 的方法
    怎样成为优秀的软件模型设计者
    Asp.NET编程时的几个小技巧
    在.net安装程序中部署oracle客户端全攻略
    在.NET中调用Oracle9i存储过程经验总结
    使用JNDI的一个容易忽略的错误
  • 原文地址:https://www.cnblogs.com/hd92/p/14841117.html
Copyright © 2020-2023  润新知