mysql秒杀业务瓶颈_浅谈不同秒杀业务规模下系统架构的演进

1.前言

关于秒杀系统的架构设计,网上有很多文章讨论。但一般没有针对负载进行量化讨论,本文将就此方面进分享一些我的思考,欢迎大家参与讨论,选择更优的解决方案。

后续我将基于一个基础系统,逐步增加秒杀业务规模(负载),评估系统的瓶颈、寻找已经解决方案,尝试逐步演进和优化秒杀系统的架构。

1.1 秒杀业务规模的指标

影响秒杀业务规模大约有以下几个因素:秒杀并发数:其本质是参与人数,越多人参与,秒杀开始时的并发数就越高。

秒杀商品数量:参与秒杀的商品数量,也就是商品的库存数量。这个数量若是比较大,并且参与人数也比较多。那势必需要短时间内创建大量订单,这对数据库是个考验。

秒杀活动数量:一个商品的秒杀活动算一个,若多个商品同时进行秒杀活动,是会增加系统的负载的。但是,两个商品各秒杀1000件和一个商品秒杀2000件,对系统的压力是有些区别的。这个问题这里先不展开细说。

其中秒杀并发数和秒杀商品数量是两个比较独立的因素,秒杀活动数量有点类似是前两个因素的复合。为了方便理解和讨论,后文我将分别讨论前两个因素对秒杀系统架构设计的影响。

1.2 基础系统的设定 & 相关服务的性能指标

在讨论后续架构的演进之前,我们先假定一个原始的基础的秒杀系统:DNS指向到一台前端机,前端机上使用Nginx做负载均衡,按ip hash分流到后端有两台LNP架构(Linux+Nginx+PHP)的web服务器。

数据库使用MySQL,主从结构,读写分离。

使用缓存服务设计了计数器,每个秒杀请求都会调用计数器,获得自己的序号。只有序号不大于秒杀商品数量的请求,才操作数据库创建订单(实际项目可能要考虑创建订单失败的情况,需要允许更多一些的序号尝试创建订单,这需要结合具体情况分析。这里先假定创建订单一定会成功)。

扣减库存时,使用以下SQL语句进行,若update影响条数为0则不创建订单,避免高并发下超卖。

sql -- stock_info 是库存信息表;其中pid是商品id,stock_num是剩余库存数量,sell_num是已售出数量 update stock_info set stock_num = stock_num -1,sell_num=sell_num+1 where pid = xx and stock_num > 0;

相关服务的性能指标我们假定如下(实际操作时,请以真实压测数据为准)

equation?tex=%5Cbegin%7Barray%7D%5Bb%5D+%7B%7Cc%7Cc%7C%7D++%5Chline+%E5%8D%95%E6%9C%BA%E6%9C%8D%E5%8A%A1+%26+%E6%9C%80%E5%A4%A7%E5%B9%B6%E5%8F%91%28QPS%29+%5C%5C++%5Chline+%E7%A7%92%E6%9D%80%E6%8E%A5%E5%8F%A3++%26+2k+++%5C%5C++%5Chline+%E7%BC%93%E5%AD%98%E6%9C%8D%E5%8A%A1%28memcache%2FRedis%29++%26+10k++%5C%5C++%5Chline+MySQL%E6%95%B0%E6%8D%AE%E5%BA%93%E8%AF%BB%E5%86%99++%26+3k+%5C%5C++%5Chline+%E8%BD%AF%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1%28Nginx%29++%26+50k+++%5C%5C+%5Chline+%5Cend%7Barray%7D%5C%5C

基于以上基础约定,后续将分别讨论秒杀并发数和秒杀商品数量对架构设计的影响。

2. 秒杀并发数对架构设计的影响

为了方便理解和简化逻辑,在讨论秒杀并发树的影响,我们把另一个因素秒杀商品数量锁定为1000保持不变,并且只有一个秒杀活动。

2.1 秒杀并发 < 4k QPS(基础系统的负载能力)

秒杀接口的最大负载为:2k*2 = 4k

缓存读写最大负载为:10k

因为有缓存计数器的筛选,到达数据库的流量为1000,不会因为秒杀并发数的增加而增加。数据库无压力问题

Nginx负载均衡:5w

通过以上分析可知,系统瓶颈在秒杀接口,那整体系统的最大可负载为4k QPS。

2.2 秒杀并发 > 4k QPS

解决方案:增加后端web服务器。若web服务器增加到20台,那秒杀接口理论上可负载的并发数是2k*20=40k

接下来的瓶颈:缓存服务。因为缓存计数器是全局的,所有的秒杀接口必须使用同一个计数器。当秒杀并发数超过10k,缓存服务将出现问题。

2.3 秒杀并发 > 10k QPS

解决方案:更换可支持更高并发的计数器服务,不过可能也会有上限。

采用分级限流的模式,类似于选秀节目的逐级比赛筛选一样。每10k流量(5台web服务器)一组,使用一个缓存计数器,筛选出前1000(商品数量)个请求进入下一级筛选,其余请求返回秒杀失败。然后下一级的筛选也是没10k流量(上一级10个计数器的筛选结果1000*10=10k)再使用一个新的计数器筛选出前1000个请求进入下一级,其余返回秒杀失败。每个计数器都不会超过自身的最大负载,增加计数器即可负载更大并发的筛选。如此逐级限流筛选,只要筛选的层级足够,理论上再多的并发请求都可以高效的筛选出最靠前的1000个请求。

下一个瓶颈:负责负载均衡的Nginx前端机

2.4 秒杀并发 > 50k QPS

解决方案:软件负载均衡的方案,可以尝试kong和openresty,一般可以提高到几百万QPS(注:来自运维朋友的数据,我未验证)

更换为硬件负载均衡,如F5,一般可以到几百万甚至上千万QPS(注:同1)

多域名轮询。部署多个前端机,分配不一样的域名。按一定的策略把秒杀请求均匀分散到不同的前端机,例如前端在获取秒杀接口地址时,随机的分配前端机域名。理论上每增加一台前端机(以及后端相应机器),可以让系统负载能力增加5w QPS。

DNS负载均衡

多种负载均衡方案组合使用

下一个瓶颈:理论上只要加机器就可以负载更大的秒杀并发。

2.5 针对秒杀并发的终极武器

经过之前的讨论,大家应该已经发现,针对秒杀并发,整体思路就是限流。限制无效的秒杀流量(未秒杀成功)穿透到更后端的服务,给后端服务减轻压力。以上的架构设计中,限流是在web服务层做的,为此增加了很多机器和逻辑判断。其实我们把限流提前到软件负载均衡或硬件负载均衡那一层做,将会更加的高效和经济。后端服务只需承接真正秒杀成功的流量,大约为秒杀商品数量。

以Nginx软件负载均衡为例,根据url(每个秒杀活动都不一样)设置不同限流数量。若某个秒杀活动的秒杀商品数量为1000,那则限流1000(实际情况业务上可能需要进行一些过滤,那可以按比例放大一下限流数量),超过限流数量的请求,则都拒绝访问,返回502。前端js则把所有返回502错误的请求,都当做是秒杀失败。

如此,当秒杀商品数量保持不变时,秒杀并发是几千QPS和几百万QPS对于后端系统的负载都是一样的,可谓是针对秒杀并发的终极武器。注:此方案我只找运维朋友确认过可以实现,具体如何实现我还仔细研究,等我研究清楚了在写文章分享。

3. 秒杀商品数量对架构设计的影响

当负载均衡入口把绝大多数无效秒杀流量(排序靠后,肯定无法秒杀成功的请求)阻挡后,后端服务所承载的压力基本就是有效的秒杀流量(排序靠前,可能秒杀成功的请求)。若活动是成功的(参与人数大于商品数量),这个有效的秒杀流量基本就是等于秒杀商品数量。秒杀商品数量越多,后端服务的压力就越大。

分析后端服务的各个环节,最先出现瓶颈的应该是数据库。这些请求基本都需扣库存创建订单,数据库的操作无法避免。读数据库压力,可以增加从库来分流,降低单个数据库的压力。写数据库的压力则无法简单增加数据库来分流,因为这些请求针对的是同一件商品,操作的是同一份库存,可能还需要配合修改表结构和调整业务代码。

写数据库的不同场景分类:insert操作,每条记录各自独立无关系。在不使用自增主键时(可以使用外部发号器生成id),可以简单分散到多库同时写入

update不同的表或记录,结合业务按一定策略分表分库,可以分散压力。

update同一条记录,所有操作都是串行,效率最低。一般的解决方案是异步处理或分表分库(这种场景的分表分库可能需要结合业务逻辑做很多的工作)

扣减库存属于第3类,若有瓶颈,解决问题的思路有两个大方向:提升update的性能,如优化现有数据库(MySQL)的update性能;或者更换更高效的数据库(如nosql、内存数据库等等)

基于MySQL调整架构,实现update分流,分散压力。分流的思路也有两种:纵向分流和横向分流。

数据库update的分流策略 纵向分流:属于时间上的分流,使用消息队列把瞬时操作改为异步操作,把流量在时间维度上拉长进行分流。适用于抹平瞬时峰值流量且允许操作有延迟的场景,局限性比较大。秒杀的场景可以使用此方案解决,但不能无限的应对流量的增长。 横向分流:属于空间上的分流,使用分表分库的方式,分散流量。理论上只要扩容就可以不断应对流量的增加。针对update同一条记录的扣减库存的场景,分表分库时还需要使用一个分桶的概念。大家可以参考以下两篇文章:高并发场景下强一致预算/库存扣减方案 和 高并发库存秒杀场景阿里巴巴数据库是这样应对的

针对数据库写场景的分表分库,方案比较复杂,并且大多都是针对特定业务场景。这个问题以后再另起文章仔细研究一下。

4. 寻找更优的方案

以上只是我个人的思考,有些方案并没有实际实施过,只是理论推导上的可行。欢迎大家找bug、提疑问、提建议,让我们在讨论交流中共同进步!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值