秒杀系统设计总结(持续更新中。。)

一、前言

秒杀系统其实是一个比较复杂的设计,文章先介绍设计秒杀系统的思路脉络和设计系统的原则。后面章节再详细介绍使用中的工具、中间件、设计方案。由于本人并没有真正实践过秒杀系统落地,了解过和实践过完全是两个概念,实践才能更好的了解某个知识,所以收集过来的观点很有可能不太正确,还请见谅,整理这篇文章的目的还是出于自身学习,也由于没有实战经验导致我挺难通过一个案例把这些知识点统一整合,知识看上去是碎片化的,但是多少能起到帮助作用

二、秒杀系统需要解决的常见问题

1)瞬时:一提到秒杀系统给人最深刻的印象是超大的瞬时并发,如果系统没有经过限流或者熔断处理,那么系统瞬间就会崩掉,就好像被DDos攻击一样。

2)超卖:秒杀除了大并发这样的难点,还有一个所有电商都会遇到的痛,那就是超卖,电商搞大促最怕什么?最怕的就是超卖,产生超卖了以后会影响到用户体验,会导致订单系统、库存系统、供应链等等,产生的问题是一系列的连锁反应,所以电商都不希望超卖发生,但是在大并发的场景最容易发生的就是超卖,不同线程读取到的当前库存数据可能下个毫秒就被其他线程修改了,如果没有一定的锁库存机制那么库存数据必然出错,都不用上万并发,几十并发就可以导致商品超卖。

3)性能:当遇到大并发和超卖问题后,必然会引出另一个问题,那就是性能问题,如何保证在大并发请求下,系统能够有好的性能,让用户能够有更好的体验,不然每个用户都等几十秒才能知道结果,那体验必然是很糟糕的。

三、设计系统的整体思路

从整体上看设计秒杀系统需要解决的两个核心问题,一个是并发读,一个是并发写,优化时也可以基于这两个核心问题进行优化。

下面是系统设计时的几个战术要点:

并发读的核心优化理念是尽量减少用户到服务端来“读”数据,或者让他们读更少的数据

并发写的处理原则也一样,它要求我们在数据库层面独立出来一个库,做特殊的处理

针对意料之外的情况设计的planB兜底方案

需要遵循几个原则:用户请求的数据尽量少、请求数尽量少、路径尽量短、依赖尽量少,并且不要有单点

另外我们也可以围绕高性能一致性高可用 这几个角度来展开架构设计

高性能设计:动静分离方案、热点的发现与隔离、请求的削峰与分层过滤、服务端的极致优化

一致性设计:秒杀减库存的方案设计保证一致性

高可用设计:兜底planB方案

四、架构原则

一、用户请求的数据尽量少

所谓“数据要尽量少”,首先是指用户请求的数据能少就少。请求的数据包括上传给系统的数据和系统返回给用户的数据(通常就是网页)。

为啥“数据要尽量少”呢?因为首先这些数据在网络上传输需要时间,其次不管是请求数据还是返回数据都需要服务器做处理,而服务器在写网络时通常都要做压缩(gzip)和字符编码,这些都非常消耗 CPU,所以减少传输的数据量可以显著减少 CPU 的使用。例如,我们可以简化秒杀页面的大小,去掉不必要的页面装修效果,等等。

其次,“数据要尽量少”还要求系统依赖的数据能少就少,包括系统完成某些业务逻辑需要读取和保存的数据,这些数据一般是和后台服务以及数据库打交道的。调用其他服务会涉及数据的序列化和反序列化,而这也是 CPU 的一大杀手,同样也会增加延时。而且,数据库本身也容易成为一个瓶颈,所以和数据库打交道越少越好,数据越简单、越小则越好。

二、请求数要尽量少

用户请求的页面返回后,浏览器渲染这个页面还要包含其他的额外请求,比如说,这个页面依赖的 CSS/JavaScript、图片,以及 Ajax 请求等等都定义为“额外请求”,这些额外请求应该尽量少。因为浏览器每发出一个请求都多少会有一些消耗,例如建立连接要做三次握手,有的时候有页面依赖或者连接数限制,一些请求(例如 JavaScript)还需要串行加载等。另外,如果不同请求的域名不一样的话,还涉及这些域名的 DNS 解析,可能会耗时更久。所以你要记住的是,减少请求数可以显著减少以上这些因素导致的资源消耗。

例如,减少请求数最常用的一个实践就是合并 CSS 和 JavaScript 文件,把多个 JavaScript 文件合并成一个文件,在 URL 中用逗号隔开(https://g.xxx.com/tm/xx-b/4.0.94/mods/??module-preview/index.xtpl.js,module-jhs/index.xtpl.js,module-focus/index.xtpl.js)。这种方式在服务端仍然是单个文件各自存放,只是服务端会有一个组件解析这个 URL,然后动态把这些文件合并起来一起返回。

三. 路径要尽量短

所谓“路径”,就是用户发出请求到返回数据这个过程中,需求经过的中间的节点数。

通常,这些节点可以表示为一个系统或者一个新的 Socket 连接(比如代理服务器只是创建一个新的 Socket 连接来转发请求)。每经过一个节点,一般都会产生一个新的 Socket 连接。

然而,每增加一个连接都会增加新的不确定性。从概率统计上来说,假如一次请求经过 5 个节点,每个节点的可用性是 99.9% 的话,那么整个请求的可用性是:99.9% 的 5 次方,约等于 99.5%。

所以缩短请求路径不仅可以增加可用性,同样可以有效提升性能(减少中间节点可以减少数据的序列化与反序列化),并减少延时(可以减少网络传输耗时)。

要缩短访问路径有一种办法,就是多个相互强依赖的应用合并部署在一起,把远程过程调用(HTTP/RPC)变成 JVM 内部之间的方法调用。

四. 依赖要尽量少

所谓依赖,指的是要完成一次用户请求必须依赖的系统或者服务,这里的依赖指的是强依赖。

举个例子,比如说你要展示秒杀页面,而这个页面必须强依赖商品信息、用户信息,还有其他如优惠券、成交列表等这些对秒杀不是非要不可的信息(弱依赖),这些弱依赖在紧急情况下就可以去掉。

要减少依赖,我们可以给系统进行分级,比如 0 级系统、1 级系统、2 级系统、3 级系统,0 级系统如果是最重要的系统,那么 0 级系统强依赖的系统也同样是最重要的系统,以此类推。注意,0 级系统要尽量减少对 1 级系统的强依赖,防止重要的系统被不重要的系统拖垮。例如支付系统是 0 级系统,而优惠券是 1 级系统的话,在极端情况下可以把优惠券给降级,防止支付系统被优惠券这个 1 级系统给拖垮。

五. 不要有单点

关键点是避免将服务的状态和机器绑定,即把服务无状态化,这样服务就可以在机器中随意移动。如何那把服务的状态和机器解耦呢?这里也有很多实现方式。例如把和机器相关的配置动态化,这些参数可以通过配置中心来动态推送,在服务启动时动态拉取下来,我们在这些配置中心设置一些规则来方便地改变这些映射关系。

参考样例介绍

淘宝早期架构

秒杀详情成为了一个独立的新系统,另外核心的一些数据放到了缓存(Cache)中,其他的关联系统也都以独立集群的方式进行部署。

淘宝早期秒杀系统

这个系统架构有下面几个要点:

把秒杀系统独立出来单独打造一个系统,这样可以有针对性地做优化,例如这个独立出来的系统就减少了店铺装修的功能,减少了页面的复杂度;

在系统部署上也独立做一个机器集群,这样秒杀的大流量就不会影响到正常的商品购买集群的机器负载;

将热点数据(如库存数据)单独放到一个缓存系统中,以提高“读性能”;

增加秒杀答题,防止有秒杀器抢单。

淘宝进一步升级的架构

为了满足更高的性能,淘宝对秒杀系统做了进一步升级来满足更高的性能,但是事实上也造成了系统的不通用性增加,系统复杂度升高。

进一步升级架构主要改造点有:

对页面进行彻底的动静分离,使得用户秒杀时不需要刷新整个页面,而只需要点击抢宝按钮,借此把页面刷新的数据降到最少。

在服务端对秒杀商品进行本地缓存,不需要再调用依赖系统的后台服务获取数据,甚至不需要去公共的缓存集群中查询数据,这样不仅可以减少系统调用,而且能够避免压垮公共缓存集群 。

增加系统限流保护,防止最坏情况发生。

进一步升级后的系统

实战演练 - 疫情期间秒杀口罩

我们设计秒杀系统时,需要根据不同级别的流量,由简单到复杂的场景打造的不同的系统架构,同时要平衡到系统的通用性和维护性,降低影响性,使系统或者其中的模块能够更好的被复用,运维简单也是需要考虑的事项。

<
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农老K

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值