如何设计一个秒杀系统

0.前言

  • 本文是许令波(淘系的高级技术专家)的一篇专栏的总结 ,截取了其中重要的部分分享出来
  • 因为是收费专栏,所以不方便在网上共享资源,如需获取资源,可私信我,但请保证是个人学习所用

1. 秒杀系统架构设计都有哪些关键点?

  • 秒杀其实主要解决两个问题,一个是并发读,一个是并发写,而且读多写少
  • 并发读的核心优化理念是尽量减少用户到服务端来“读”数据,或者让他们读更少的数据(其实不止服务端,最重要的是减少对数据库的访问,一般我们做的秒杀项目的瓶颈都在数据库)
  • 并发写的处理原则也一样,它要求我们在数据库层面独立出来一个库,做特殊的处理(数据库独立出来通常是针对淘宝这样超大的并发,像我们平时做的小项目则基本不需要,只需要做好缓存即可,减少最终打到数据库的流量)
  • 另外,还要针对秒杀系统做一些保护,针对意料之外的情况设计兜底方案,以防止最坏的情况发生,一般是采用限流、降级等方法
  • 总结一下:
    • 秒杀是一个典型的读多写少的场景,也是一个典型的高并发的场景
    • 秒杀的高并发与平时所讲的高并发还有一些不同,因为秒杀并不需要处理所有的请求,秒杀系统更像一个逐级的大坝,不断拦截上流的洪水,使得下游(数据库)可以保证安全
    • 解决高并发的核心思想是缓存+异步,缓存可以包含各级缓存,前端、CDN、服务器等等,异步则可以使用消息队列

2. 设计秒杀系统时应该注意的5个架构原则

2.1 架构原则:“4 要 1 不要”

2.2 数据要尽量少

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

2.3 请求数要尽量少

  • 用户请求的页面返回后,浏览器渲染这个页面还要包含其他的额外请求,比如说,这个页面依赖的 CSS/JavaScript、图片,以及 Ajax 请求等等都定义为“额外请求”,这些额外请求应该尽量少
  • 因为浏览器每发出一个请求都多少会有一些消耗,例如建立连接要做三次握手,有的时候有页面依赖或者连接数限制,一些请求(例如 JavaScript)还需要串行加载等。另外,如果不同请求的域名不一样的话,还涉及这些域名的 DNS 解析,可能会耗时更久
  • 减少请求数最常用的一个实践就是合并 CSS 和 JavaScript 文件,把多个 JavaScript 文件合并成一个文件,在 URL 中用逗号隔开

2.4 路径要尽量短

  • 所谓“路径”,就是用户发出请求到返回数据这个过程中,需求经过的中间的节点数
  • 缩短请求路径不仅可以增加可用性(减少数据丢失),同样可以有效提升性能
  • 要缩短访问路径有一种办法,就是多个相互强依赖的应用合并部署在一起,把远程过程调用(RPC)变成 JVM 内部之间的方法调用

2.5 依赖要尽量少

  • 所谓依赖,指的是要完成一次用户请求必须依赖的系统或者服务,这里的依赖指的是强依赖
  • 举个例子,比如说你要展示秒杀页面,而这个页面必须强依赖商品信息、用户信息,还有其他如优惠券、成交列表等这些对秒杀不是非要不可的信息(弱依赖),这些弱依赖在紧急情况下就可以去掉
  • 要减少依赖,我们可以给系统进行分级,比如 0 级系统、1 级系统、2 级系统、3 级系统,0 级系统如果是最重要的系统,那么 0 级系统强依赖的系统也同样是最重要的系统(这些都是高级系统需要考虑的,小项目没这么复杂。。。)

2.6 不要有单点

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

3. 如何才能做好动静分离?有哪些方案可选?

3.1 何为动静数据

  • 所谓“动静分离”,其实就是把用户请求的数据(如 HTML 页面)划分为“动态数据”和“静态数据”

  • “动态数据”和“静态数据”的主要区别就是看页面中输出的数据是否和 URL、浏览者、时间、地域相关,以及是否含有 Cookie 等私密数据

  • 比如说:

    • 很多媒体类的网站,某一篇文章的内容不管是你访问还是我访问,它都是一样的。所以它就是一个典型的静态数据,但是它是个动态页面
    • 我们如果现在访问淘宝的首页,每个人看到的页面可能都是不一样的,淘宝首页中包含了很多根据访问者特征推荐的信息,而这些个性化的数据就可以理解为动态数据了

3.2 怎样对静态数据做缓存?

  • 应该把静态数据缓存到离用户最近的地方。静态数据就是那些相对不会变化的数据,因此我们可以把它们缓存起来。缓存到哪里呢?常见的就三种,用户浏览器里、CDN 上或者在服务端的 Cache 中。你应该根据情况,把它们尽量缓存到离用户最近的地方

3.3 从以下 5 个方面来分离出动态内容:

  • URL 唯一化。商品详情系统天然地就可以做到 URL 唯一化,比如每个商品都由 ID 来标识,那么 http://item.xxx.com/item.htm?id=xxxx 就可以作为唯一的 URL 标识。为啥要 URL 唯一呢?前面说了我们是要缓存整个 HTTP 连接,那么以什么作为 Key 呢?就以 URL 作为缓存的 Key,例如以 id=xxx 这个格式进行区分
  • 分离浏览者相关的因素。浏览者相关的因素包括是否已登录,以及登录身份等,这些相关因素我们可以单独拆分出来,通过动态请求来获取
  • 分离时间因素。服务端输出的时间也通过动态请求获取
  • 异步化地域因素。详情页面上与地域相关的因素做成异步方式获取,当然你也可以通过动态请求方式获取,只是这里通过异步获取更合适
  • 去掉 Cookie。服务端输出的页面包含的 Cookie 可以通过代码软件来删除,如 Web 服务器 Varnish 可以通过 unset req.http.cookie 命令去掉 Cookie。注意,这里说的去掉 Cookie 并不是用户端收到的页面就不含 Cookie 了,而是说,在缓存的静态数据中不含有 Cookie

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

4.1 流量削峰的一些操作思路:排队、答题、分层过滤

4.2 排队

  • 要对流量进行削峰,最容易想到的解决方案就是用消息队列来缓冲瞬时流量,把同步的直接调用转换成异步的间接推送,中间通过一个队列在一端承接瞬时的流量洪峰,在另一端平滑地将消息推送出去

4.3 答题

  • 第一个目的是防止部分买家使用秒杀器在参加秒杀时作弊
  • 第二个目的其实就是延缓请求,起到对请求流量进行削峰的作用,从而让系统能够更好地支持瞬时的流量高峰。这个重要的功能就是把峰值的下单请求拉长,从以前的 1s 之内延长到 2s~10s

4.4 分层过滤

  • 大部分数据和流量在用户浏览器或者 CDN 上获取,这一层可以拦截大部分数据的读取
  • 经过第二层(即前台系统)时数据(包括强一致性的数据)尽量得走 Cache,过滤一些无效的请求
  • 再到第三层后台系统,主要做数据的二次检验,对系统做好保护和限流,这样数据量和请求就进一步减少
  • 最后在数据层完成数据的强一致性校验

5. 秒杀系统“减库存”设计的核心逻辑

5.1 减库存操作一般有如下几个方式:

  • 下单减库存,即当买家下单后,在商品的总库存中减去买家购买数量
  • 付款减库存,即买家下单后,并不立即减库存,而是等到有用户付款后才真正减库存,否则库存一直保留给其他买家
  • 预扣库存,这种方式相对复杂一些,买家下单后,库存为其保留一定的时间(如 10 分钟),超过这个时间,库存将会自动释放,释放后其他买家就可以继续购买

5.2 大型秒杀中如何减库存?

  • 目前来看,业务系统中最常见的就是预扣库存方案,像你在买机票、买电影票时,下单后一般都有个“有效付款时间”,超过这个时间订单自动释放,这都是典型的预扣库存方案
  • 保证数据库中的库存字段值不能为负数(超卖),一般我们有多种解决方案:
    • 一种是在应用程序中通过事务来判断,即保证减后库存不能为负数,否则就回滚
    • 另一种办法是直接设置数据库的字段数据为无符号整数,这样减后库存字段值小于零时会直接执行 SQL 语句来报错
    • 再有一种就是使用判断语句(最简单的只需要在SQL语句之后加上判断库存where 数量>0),例如:update goods set num = num - 1 WHERE id = 1001 and num > 0

6.兜底方案

6.1 降级、限流和拒绝服务

6.2 降级

  • 所谓“降级”,就是当系统的容量达到一定程度时,限制或者关闭系统的某些非核心功能,从而把有限的资源保留给更核心的业务
  • 降级方案可以这样设计:当秒杀流量达到 5w/s 时,把成交记录的获取从展示 20 条降级到只展示 5 条。“从 20 改到 5”这个操作由一个开关来实现,也就是设置一个能够从开关系统动态获取的系统参数

6.3 限流

  • 如果说降级是牺牲了一部分次要的功能和用户的体验效果,那么限流就是更极端的一种保护措施了。限流就是当系统容量达到瓶颈时,我们需要通过限制一部分流量来保护系统,并做到既可以人工执行开关,也支持自动化保护的措施

6.4 拒绝服务

  • 如果限流还不能解决问题,最后一招就是直接拒绝服务了
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值