阿里P8架构师谈:实战讲解高并发和秒杀抢购系统设计

互联网特别是电商平台,阿里双11秒杀、还有12306春运抢票、以及平时各类节假日抢购活动等,都是典型的高并发场景。这类场景最大的特征就是活动周期短,瞬间流量大(高并发),大量的人短时间涌入服务器抢购,可是数量有限,最终只有少数人能成功下单。php

这里,就来说一讲对应该场景下须要考虑的技术实现。先从基本的概念的创建,再讲对应的实现部分。前端

 

第一:高并发java

技术要作的事,一方面优化程序,让程序性能最优,单次请求时间能从50ms优化到25ms,那就能够在一秒钟内成功响应翻倍的请求了。另外一方面就是增长服务器,用更大的集群来处理用户请求,设计好一个可靠且灵活扩充的分布式方案就更加剧要了。mysql

 

第二:时间短golang

火热的秒杀活动,真的是一秒钟之内就会把商品抢购一空,而大部分用户的感觉是,提交订单的过程却要等待好几秒、甚至十几秒,更糟糕的固然是请求报错。那么一个好的秒杀体验,固然但愿尽量减小用户等待时间,准确的提示用户当前是否还有商品库存。而这些,也是须要有优秀的程序设计来保证的。面试

 

第三:系统容量预估redis

系统设计的时候,都须要有一个容量预估,那就是要提早计算好,咱们设计的系统,要承载多大的数量级。sql

假如:数据库

线上前端服务器规格是8核16G内存的服务器,而提交订单的处理程序耗时100ms,那么能够简单计算一下。缓存

每秒能够处理的订单请求数=1000ms/100ms*8=80qps

上面这个结果,对于秒杀系统来讲,确定是很是不理想的。

  • 若是能将处理程序耗时优化后,下降到10ms,那么就能够达到800qps。
  • 若是咱们能够把程序继续优化,能快速区分开有库存和无库存处理,那么无库存时处理就有可能作到1ms甚至更低的耗时。这样无库存时就能有更好的性能,上万的qps也是能够达到的。

上面的预估,都是针对单机,那么简单的增长前端服务器,是否是就能有更好的并发处理量呢?确定没这么简单,由于数据库、缓存系统甚至机房网络带宽都会成为瓶颈。因而就要有一个更好的分布式方案。

 

第四:好的分布式方案

一个好的分布式方案,首先固然是稳定可靠,不要出乱子,而后就是方便扩充,最好的效果固然是增长一台服务器,并发处理量能够1:1线性增加。

好比:单机qps是1k,那么10台服务器能够作到1w,100台能够作到10w每秒。

要作到这样的线性增加效果,就要杜绝出现瓶颈,不然仍是会代价太大。

拒绝假的分布式尤为重要,好比:前端服务器是能够独立存在的,可是都依赖集中的一个数据库或者缓存系统,那最后,必定是集中的那个数据库或者缓存系统受不了,一样没法作到一个好的分布式。

 

第五:关注系统的瓶颈

你们先有几个基本的共识,系统的处理速度

  • 程序内数据读写 > redis > mysql > 磁盘

  • 单机网络请求 > 局域网内请求 > 跨机房请求

咱们优化程序的时候,尽可能用最快的方式,尽可能用最简短的逻辑。

用redis替代mysql来保存订单处理中依赖的数据,用程序中的提交的数据代替从redis中二次获取数据,好比:商品库存信息,用户订单信息。

逻辑处理中,把速度快且提早中断的逻辑放在最前面,好比:验证登陆,验证问答。

咱们作分布式方案的时候,尽可能把资源调用放在最近的地方。

前端服务器依赖的数据尽可能就在局域网内,若是能在单机都有读的redis服务固然更好,程序维护数据响应会复杂些。

不要出现跨机房网络请求,不要出现跨机房网络请求,不要出现跨机房网络请求,重要的事情说三遍。

 

第六:什么语言更适合这类系统

固然,像是用golang, ngx_lua可能在高并发和性能方面会更有优点。

若是使用java、php固然也是能够的,做为一个系统,语言只是工具,更好的设计和优化,才能达到最终想要的效果。

有了上面的基本概念,咱们接下来再来看看,具体运行时,会出现什么情况。

 

下面是一些具体的实现问题:

 

问题:库存超卖

只有10个库存,可是一秒钟有1k个订单,怎么能不超卖呢?

核心思想就是保证库存递减是原子性操做,10--返回9,9--返回8,8--返回7。

而不能是读取出来库存10,10-1=9再更新回去。由于这个读取和更新是并发执行的,极可能就会有1k个订单都成功了,而库存实际只有10。

那么,怎么保证原子性操做呢?

 

1 数据库

update product set left_num=left_num-1 where left_num>0;

这里用到的是left_num=left_num-1,若是left_num>0才能执行成功,数据库查询、更新的时候有用到锁,是能够保证更新操做的原子性的。

数据库性能较差,不建议使用。

 

2 分布式锁

用redis来作一个分布式锁,reids->setnx('lock', 1) 设置一个锁,程序执行完成再del这个锁。

锁定的过程,不利于并发执行,你们都在等待锁解开,不建议使用。

 

3 消息队列

将订单请求所有放入消息队列,而后另一个后台程序一个个处理队列中的订单请求。

并发不受影响,可是用户等待的时间较长,进入队列的订单也会不少,体验上并很差,也不建议使用。

 

4 redis递减

经过 redis->incrby('product', -1) 获得递减以后的库存数。

 

问题:集群怎么来规划

前端服务器由于没有相互间关联,集群的数量不受影响。redis的性能能够达到每秒几万次响应,因此一个集群的规模,也就是redis服务能够承载的数量。

好比:一台前端服务器是1~2k的qps(有库存时),那么10台+1台redis就能够是一个独立的集群,能够支撑1~2w每秒订单量。

10个上述的集群就能够作到一秒钟处理10w~20w的有效订单。

若是秒杀活动的库存量在1w之内,预计参与的人数在百万左右,那么有一个集群也就能够搞定。

若是秒杀参与的人数超过千万,那么就要用到不止一个集群了。

 

问题:多个集群的数据怎么保持一致性

不要作多集群的数据同步,而是用散列,每一个集群的数据是独立存在的。

假设,有10个商品,每一个商品有1w库存,规划用10个集群,那么每一个集群有10个商品,每一个商品是1k库存。

每一个集群只须要负责把本身的库存卖掉便可,至于说,会不会有用户知道有10个集群,而后每一个集群都去抢。

这种状况就不要用程序来处理了,利用运营规则,活动结束后汇总订单的时候再去处理就行了。

若是担忧散列的不合理,好比:某个集群用户访问量特别少,那么能够引入一个中控服务,来监控各个集群的库存,而后再作平衡。

 

问题:机器人抢购怎么办

没什么太好的办法,相似DDOS攻击,只能是让自身更强大才是王道。

运营策略上,能够严格控制用户注册,必须登陆,提交订单的时候引入图像验证码,问答,交互式验证等。

 

---end---

往期热文:

高并发架构系列:如何从0到1设计一个MQ消息队列

高并发架构系列:Redis缓存和MySQL数据一致性方案详解

【精选】278道高级Java高频面试题目+答案,通关中大型互联网企业高级工程师必备

相关文章
相关标签/搜索