前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何设计一个秒杀系统

如何设计一个秒杀系统

作者头像
憧憬博客
发布2020-10-09 11:23:57
7310
发布2020-10-09 11:23:57
举报
文章被收录于专栏:憧憬博客分享憧憬博客分享

如何设计一个秒杀系统

秒杀已经成为电商不可缺少的一步分了,所谓 买到就是赚到,可以成功吸引到一大堆用户,那程序员面对这些用户该怎么办呢。我们该如何设计秒杀呢?

在设计秒杀前,我们需要先理解秒杀,秒杀其实主要就是解决两个问题:并发读并发写

  • 并发读的核心优化理念是尽量减少用户到服务端来 "读" 数据,或者让他们读更少的数据;
  • 并发写的处理原则也一样,它要求我们在数据库层面独立出来一个库,做特殊的处理。

另外,我们还要针对秒杀系统做一些保护,针对意料之外的情况设计兜底方案,以防止最坏的情况发生。

然后我们升级到架构层面看,就是需要保证架构的几个常见的质量属性:高性能高可用一致性

  • 高性能

秒杀涉及大量的并发读和并发写,因此支持高并发访问这点非常关键。我们从设计数据的动静分离方案、热点的发现与隔离、请求的削峰与分层过滤、服务端的极致优化等。

  • 一致性

秒杀中商品减库存的实现方式同样关键。可想而知,有限数量的商品在同一时刻被很多倍的请求同时来减库存,减库存又分为 拍下减库存 付款减库存,在大并发更新的过程中都要保证数据的准确 性。

  • 高可用

虽然我们可以对系统进行优化,但现实中总难免出现一些我们考虑不到的情况,所以要保证系统的高可用和正确性,我们还要设计一个 PlanB 来兜底,以便在最坏情况发生时仍然可以让我们不被开除。

初期架构轮廓

如何构建一个超大流量并发读写、高性能,以及高可用的系统,4要1不要

数据要尽量少

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

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

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

请求数要尽量少

用户请求的页面返回后,浏览器渲染这个页面还要包含其他的额外请求.

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

例如,减少请求数最常用的一个实践就是合并 CSSJavaScript 文件,把多个 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%。 所以缩短请求路径不仅可以增加可用性,同样可以有效提升性能 减少中间节点可以减少数据的序列化与反序列化,并减少延时(可以减少网络传输耗时)。 要缩短访问路径有一种办法,就是多个相互强依赖的应用合并部署在一起,把远程过程调用RPC 变成 JVM 内部之间的方法调用。

依赖要尽量少

所谓依赖,指的是要完成一次用户请求必须依赖的系统或者服务,这里的依赖指的是强依赖。 举个例子,比如说你要展示秒杀页面,而这个页面必须强依赖商品信息用户信息,还有其他如优惠券成交列表等这些对秒杀不是非要不可的信息(弱依赖),这些弱依赖在紧急情况下就可以去掉。

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

不要有单点

系统中的单点可以说是系统架构上的一个大忌,因为单点意味着没有备份,风险不可控,我们设计分布式系统最重要的原则就是 消除单点

  • 那如何避免单点呢?

我认为关键点是避免将服务的状态和机器绑定,即把服务无状态化,这样服务就可以在机器中随意移动。

  • 如何那把服务的状态和机器解耦呢?这里也有很多实现方式

例如把和机器相关的配置动态化,这些参数可以通过配置中心来动态推送,在服务启动时动态拉取下来,我们在这些配置中心设置一些规则来方便地改变这些映射关系。 应用无状态化是有效避免单点的一种方式,但是像存储服务本身很难无状态化,因为数据要存储在磁盘上,本身就要和机器绑定,那么这种场景一般要通过冗余多个备份的方式来解决单点问题。

如何才能做好动静分离

静态资源压缩+cdn+缓存

在商家创建完秒杀产品后, 缓存就有了, 如果修改内容, 就直接更新缓存, 秒杀开始后, 商家就不能再修改了

有针对性地处理好系统的"热点数据"

为什么要关注热点

首先,热点请求会大量占用服务器处理资源,虽然这个热点可能只占请求总量的亿分之一,然而却可能抢占 90% 的服务器资源,如果这个热点请求还是没有价值的无效请求,那么对系统资源来说完全是浪费。

热点数据 静态和动态

静态热点数据是可以预知的,例如一直去请求秒杀的商品都是相同的。 动态数据指某些数据并非我们提前知道的,例如某个商品由于某个娱乐新闻成为top 1,这个时候商品的所带来的请求也会是top 1, 对于这种动态的热点数据我们没有办法预知,只能做好限制和保护等操作

最重要最简单的方式就是独立出来一个集群,单独处理热点数据。

流量削峰这事应该怎么做?

实际上在现实生活中也有削峰的例子,例如北京开车限号、进京证、限时限行

方案有:

  • 排队 上游洪水堆积,下游平稳放水,防止洪水伤害服务器
  • 答题 抢购前先答题,实际上就类似于验证码,但不是验证码,现在验证码识别率高,防止非正常用户的请求造成服务器的过高负载,防作弊、延缓请求
  • 分层过滤,每一层设置一些条件,可以是随机数、或者秒杀资格

减库存

付款减库存

即买家下单后,并不立即减库存,而是等到有用户付款后才真正减库存,否则库存一直保留给其他买家。但因为付款时才减库存,如果并发比较高,有可能出现买家下单后付不了款的情况,因为可能商品已经被其他人买走了。 还有一个就是第三方支付的异步机制,有可能支付后没有库存,需要给用户退款

选用这种方案就需要牺牲用户的体验

下单扣库存

即当买家下单后,在商品的总库存中减去买家购买数量。下单减库存是最简单的减库存方式,也是控制最精确的一种,下单时直接通过数据库的事务机制控制商品库存,这样一定不会出现超卖的情况。但是你要知道,有些人下完单可能并不会付款。

设置订单有效时间,但是还会存在恶意下单,我们可以采用一些方案来进行制止

例如,给经常下单不付款的买家进行识别打标(可以在被打标的买家下单时不减库存)、给某些类目设置最大购买件数(例如,参加活动的商品一人最多只能买 3 件),以及对重复下单不付款的操作进行次数限制等。还可以采用定金的营销形式来减少抢购时的流量

下单减库存 在数据一致性上,主要就是保证大并发请求时库存数据不能为负数,也就是要保证数据库中的库存字段值不能为负数,一般我们有多种解决方案:

  • 设置数据库的字段数据为无符号整数,这样减后库存字段值小于零时会直接执行 SQL 语句来报错
  • 悲观锁
  • 乐观锁 版本号机制
  • 再有一种就是使用 sql 判断语句,例如这样的 SQL 语句
代码语言:javascript
复制
udpate goods set available = available - 1 where id = xx and available - 1 >= 0 ;

为了提高吞吐量,我们还可以根据商品 ID 进行分库分表设计, 将压力分布到其他的服务器; 还可以提前下好订单,将订单写入到 redislist ,然后来一个 pop 一个

  • 使用缓存处理库存

如果库存放缓存,缓存必须是高可用的,数据丢失怎么办,所以必须要多机房备份或者限流保护,如果出现极端情况,应当立马下架该商品

利用缓存来减轻数据库的压力,可以在缓存扣除后通过消息修改数据库,也或者设置多少时间去统一同步数据库

兜底方案的设计

没有人可以提前预估到所有情况

  • 架构阶段:架构阶段主要考虑系统的可扩展性和容错性,要避免系统出现单点问题。例如多机房单元化部署,即使某个城市的某个机房出现整体故障,仍然不会影响整体网站的运转。
  • 编码阶段:编码最重要的是保证代码的健壮性,例如涉及远程调用问题时,要设置合理的超时退出机制,防止被其他系统拖垮,也要对调用的返回结果集有预期,防止返回的结果超出程序处理范围,最常见的做法就是对错误异- 常进行捕获,对无法预料的错误要有默认处理结果。
  • 测试阶段:测试主要是保证测试用例的覆盖度,保证最坏情况发生时,我们也有相应的处理流程。
  • 发布阶段:发布时也有一些地方需要注意,因为发布时最容易出现错误,因此要有紧急的回滚机制。
  • 运行阶段:运行时是系统的常态,系统大部分时间都会处于运行态,运行态最重要的是对系统的监控要准确及时,发现问题能够准确报警并且报警数据要准确详细,以便于排查问题。
  • 故障发生:故障发生时首先最重要的就是及时止损,例如由于程序问题导致商品价格错误,那就要及时下架商品或者关闭购买链接,防止造成重大资产损失。然后就是要能够及时恢复服务,并定位原因解决问题。

降级

所谓降级,就是当系统的容量达到一定程度时,限制或者关闭系统的某些非核心功能,从而把有限的资源保留给更核心的业务。它是一个有目的、有计划的执行过程,所以对降级我们一般需要有一套预案来配合执行。如果我们把它系统化,就可以通过预案系统和开关系统来实现降级。

  • 降级方案可以这样设计

当秒杀流量达到 5w/s 时,把成交记录的获取从展示 20 条降级到只展示 5 条。从 20 改到 5 这个操作由一个开关来实现,也就是设置一个能够从开关系统动态获取的系统参数。 是一个不得已而为之的举措。

限流

客户端限流,好处可以限制请求的发出,通过减少发出无用请求从而减少对系统的消耗。缺点就是当客户端比较分散时,没法设置合理的限流阈值:如果阈值设的太小,会导致服务端没有达到瓶颈时客户端已经被限制;而如果设的太大,则起不到限制的作用。 服务端限流,好处是可以根据服务端的性能设置合理的阈值,而缺点就是被限制的请求都是无效的请求,处理这些无效的请求本身也会消耗服务器资源。

例如我们的系统最高支持 1w QPS 时,可以设置 8000 来进行限流保护。

拒绝服务

如果限流还不能解决问题,最后一招就是直接拒绝服务了。 当系统负载达到一定阈值时,例如 CPU 使用率达到 90% 或者系统 load 值达到 2*CPU 核数时,系统直接拒绝所有请求,这种方式是最暴力但也最有效的系统保护方式。

拒绝服务可以说是一种不得已的兜底方案,用以防止最坏情况发生,防止因把服务器压跨而长时间彻底无法提供服务。像这种系统过载保护虽然在过载时无法提供服务,但是系统仍然可以运作,当负载下降时又很容易恢复,所以每个系统和每个环节都应该设置这个兜底方案,对系统做最坏情况下的保护。

结语

我们针对不同场景有不同的架构,任何系统并非一蹴而就,当用户量不大的时候缓存+数据库就可以解决大量请求,我们还需要对自己系统的性能有个清晰的认识,每秒的 qps tps需要尽可能准确,做架构时就可以做出合适的方案。

如果当前设计满足不了业务的时候,我们需要对架构进行升级,可以将秒杀系统设计成为一个单独的系统,然后使用负载均衡分摊请求

对于秒杀的场景来说,不同 QPS 量级下瓶颈也会不一样,10w 级别可能瓶颈就在数据读取上,通过增加缓存一般就能解决,如果要到 100w 那么,可能服务端的网络可能都是瓶颈,所以要把大部分的静态数据放到 cdn 上甚至缓存在浏览器里

所以要做架构升级,还是主要要分析在预估的 QPS 下,整个系统的瓶颈会在什么地方,要针对这起瓶颈来重新设计架构方案。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 如何设计一个秒杀系统
    • 初期架构轮廓
      • 数据要尽量少
      • 请求数要尽量少
      • 路径要尽量短
      • 依赖要尽量少
      • 不要有单点
    • 如何才能做好动静分离
      • 有针对性地处理好系统的"热点数据"
        • 为什么要关注热点
        • 热点数据 静态和动态
      • 流量削峰这事应该怎么做?
        • 减库存
          • 付款减库存
          • 下单扣库存
        • 兜底方案的设计
          • 降级
          • 限流
          • 拒绝服务
        • 结语
        相关产品与服务
        数据库
        云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档