上帝视角:秒杀系统全貌 阅读10分钟

简介: 上帝视角:秒杀系统全貌 阅读10分钟

640.png

秒杀系统的全貌



秒杀这一业务场景已经发展多年,有套路可循。另外,秒杀属于极端大流量场景,它的应对经验对Web大流量应对方案有很好的借鉴意义。


1. 秒杀系统本质


秒杀正常的业务流程:查询商品 -> 创建订单 -> 减库存 -> 更新订单 -> 付款 -> 卖家发货。

而业务特性是:


  1. 低廉价格;
  2. 大幅推广;
  3. 瞬时售空;
  4. 一般是定时上架;
  5. 时间短、瞬时并发量高。


从技术角度看秒杀系统本质上是一个满足大并发、高性能和高可用的分布式系统。秒杀其实主要解决两个问题,一个是并发读,一个是并发写。并发读的核心优化理念是尽量减少用户到服务端来“读”数据,或者让他们读更少的数据;并发写的处理原则也一样,它要求我们在数据库层面独立出来一个库,做特殊的处理。另外,我们还要针对秒杀系统做一些保护,针对意料之外的情况设计兜底方案,以防止最坏的情况发生。


总的来说,架构是一种平衡的艺术,而最好的架构一旦脱离了它所适应的场景,一切都将是空谈。具体设计应该参照秒杀预估流量:


  1. QPS 小于1W:只需要把商品购买页面增加一个定时上架功能,仅在秒杀开始时才让用户看到购买按钮,当商品库存卖完了也就结束了。
  2. 随着请求量加大(QPS 1W/s -> 10W/s),这个简单的架构就很快遇到了瓶颈,因此需要做架构改造来提升系统性能。

640.png

  1. QPS 100W/s 以上
  2. 640.png

2. 怎样设计降低服务压力


2.1 架构设计原则


2.1.1 数据要尽量少


  1. 用户请求的数据能少就少。具体包含上传给系统的数据和系统返回给用户的数据。原因是首先这些数据在网络传输需要时间,其数据传输都需要服务器做压缩和字符编码,都非常消耗CPU,所以减少传输的数据量可以显著减少CPU的使用。
  2. 系统依赖的数据能少就少。依赖的路径越多会增加CPU处理时间(序列化和反序列化),同样会增加延时。


常见设计手段为:动静分离


具体为变刷新整个页面为只点击“秒杀”按钮就够了。动静分离后,客户端大幅度减少了请求的数据量。


分离改造核心:分离出动态数据。如url唯一化,分离浏览者相关因素,分离时间因素,异步化地域因素,去掉cookie等。


  1. 对静态数据缓存
  • 静态数据缓存到离用户最近的地方。浏览器、CDN、服务端Cache。
  • 静态化改造直接缓存HTTP连接
  • Web服务器流入Nginx缓存静态数据优于Tomcat。
  1. 对动态数据缓存:
  • ESI(edge side includes)服务端拼接动静态内容,组装一起返回,服务端性能有影响,但是客户端体验好
  • CSI(client side include)客户端发起异步js请求,服务端性能好,客户端可能会有延时,体验稍差.


部署架构:

640.png

需要解决(失效问题,命中率问题,发布更新问题),其他细节:浏览器缓存和cdn缓存差别很大;合并是否用gzip压缩。


2.2.2 请求数要尽量少


用户请求的页面返回后,浏览器渲染这个页面还包含其他的额外请求。例如页面依赖的CSS/JS, 图片, Ajax请求等都被定义为“额外请求”,这些额外请求应该尽量少。因为上述每个资源请求都能增加连接(需要做三次握手),可能造成资源串行加载,不同域名还有DNS解析。解决办法:合并CSS/JS文件。


常见设计手段为:流量削峰


2.2.1 流量削峰


本质上:延缓用户请求的发出。让服务处理更加平稳,节省服务器成本。削峰基本思路如下:

  • 排队:用MQ来缓冲瞬时流量,把同步的直接调用转换成异步的间接推送,中间通过一个队列在一段承接瞬时的流量洪峰,在另一端平滑的将消息推送出去。

640.png

除了利用MQ,还可以使用线程池加锁方式实现排队,FIFO内存排队。这样就会存在异步返回结果问题:解决方案有两种1. 客户端轮询,例如支付页面,每秒轮询一次;2.服务端push结果。需要C/S保持长连接。


  • 答题:防作弊,延缓请求。
  • 分层过滤:对请求进行分层过滤,讲请求尽量拦截在系统上游。传统秒杀系统之所以挂,请求都压到在后端数据库层,数据读写锁冲突严重,并发响应高,几乎所有请求都超时。流量虽大,下单成功的有效流量甚小。分层过滤其实就是采用“漏斗式”设计来处理请求。核心思想为:在不同层次尽量过滤掉无效请求[根据库存判断无法抢到商品的人],让“漏斗”最末端的才是有效请求。
  • 读系统尽量减少一致性校验的瓶颈,但尽量将不影响性能的检查条件提前
  • 写系统主要对写数据进行一致性检查


640.png


2.3.3 路径尽量短


路径:用户发出请求到返回数据这个过程中,需要经过的中间节点数。


这是因为每增加一个连接都会增加新的不确定性。从概率统计上说,假如一个请求经过5个节点,每个节点可用性是99.9%的话,那么整个请求的可用性是:99.9的5次方,约等于99.5%。缩短路径不仅可以增加可用性,同样可以有效提升性能(减少中间节点可以减少数据的序列化和反序列化),并减少延时。


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


2.4.4 依赖要尽量少,系统分级


展示秒杀页面,这个页面必须强依赖商品信息、用户信息,还有其他如优惠券、成交列表等这些对秒杀不是非要不可的信息(弱依赖),这些弱依赖在紧急情况下就可以去掉。要减少依赖就必须对系统进行分级。0级系统要尽量减少对1级系统的强依赖,防止重要的系统被不重要的系统拖垮。在极端情况下可以把1级系统例如优惠券系统降级。


2.5.5 不要有单点


无单点的重点是避免将服务的状态和机器绑定,即把服务无状态化。


应用无状态化是有效避免单点的一种方式,但是像存储服务本身很难无状态话,因为数据要存储在磁盘上,本身就要和机器绑定,那么这种场景只能通过冗余多个备份来解决单点问题。


3. 热点数据处理


为什么要处理热点数据?热点请求会大量占用服务器处理资源,虽然这个热点可能只占请求总量的亿分之一,然而却可能抢占90%的服务器资源。


什么是热点:热点分为热点操作和热点数据。


  • 热点操作,例如大量的刷新页面、大量的添加购物车、双十一零点大量的下单。对系统来说,这些操作可以抽象为“读请求”和“写请求”。热点操作中的写操作将下面单独一节讲解。
  • 热点数据:用户的热点请求对应的数据。热点数据分为“静态热点数据”和“动态热点数据”。
  • 静态热点数据:可以提前预测的热点数据。业务场景,通过卖家报名来打标。还可以通过数据分析历史成交记录,用户购物车记录分析出热点商品。
  • 动态热点数据:不能被提前预测的热点数据,系统在运行过程中临时产生的热点。例如上家临时做了广告导致的热点数据。解决方案:构建数据动态发现系统,分析热点Key,数据上报统计。


处理热点数据:


  • 一、优化 :缓存。热点数据动静分离。
  • 二、限制 :热点数据限制到一个请求队列里,防止热点数据占用太多服务器资源导致其他请求无法处理。
  • 三、隔离
  • 系统隔离:为避免对现有网站业务的冲击:分组部署,将热点描述请求分到单独的集群。秒杀系统只是一个短时的促销活动,具有时间短、访问量高的特点。如果模块与原业务系统部署在一起,将对现有的业务造成冲击。因此,应当把秒杀模块迁移出去,独立部署。
  • 数据隔离:热点秒杀数据启用单独的Cache/MySQL集群。
  • 业务隔离:卖家报名秒杀提前感知热点,做数据预热。


4. 性能优化


核心:降低CPU消耗。


4.1 衡量指标


总QPS = (1000ms / RT) * 线程数量

其中线程数量一般默认配置为 2*CPU核数 + 1。


4.2 优化方法


  • 减少编码
  • 减少序列化
  • 服务优化(如nginx返回静态数据,框架定制优化)
  • 并发读优化:应用层的LocalCache,在秒杀系统的单机上缓存商品相关的数据.
  • 静态数据(秒杀前全机推静态cache数据)
  • 动态数据(类似库存,一般缓存几秒,被动失效,允许一定的脏数据)
  • 流程:发现数据,减少短板,数据分级,减少中间环节,做好应用基线(性能基线,成本基线,链路基线)不断调整


5. 并发写-减库存


秒杀系统设计除了上述的并发读的问题,还有一个难点是如何解决并发写 – 多个用户在同时抢一件商品,也就是并发很高,但集中在同一商品上,造成实质为串行操作。因为在数据库这层本质执行的是对同一件商品扣库存 – 需要合理的减库存。用户的购买过程一般分两步:下单和付款。


BEGIN 
UPDATE stock SET count = count - 1 WHERE skuId = ?
COMMIT


减库存一般有三种方式:

  1. 下单减库存。下单减库存最简单也是控制最精确的一种,下单时直接通过数据库的事务机制控制商品库存,这样一定不会出现超卖情况。缺点是:恶意下单(有些人下单完不一定会付款,但是库存已经扣了,会影响商家。)
  2. 付款减库存。等到用户付款完后才真正减库存,否则库存一直保留给其他买家。缺点是出现买家下单后无法付款。缺点:可能超卖。
  3. 预扣库存。买家下单后库存为其保留一段时间,超过这个时间,库存会自动释放。释放后其他买家继续购买。在买家付款前,系统会校验该订单的库存是否还有保留,有保留则尝试预扣,如果预扣失败,则不允许付款;如果预扣成功,则完成付款减库存。缺点:也可能恶意下单(只能结合安全和反作弊,标示用户并限制操作) 。


一般情况下秒杀减库存逻辑复杂,存在SKU库存和总库存联动关系,需要使用MySQL事务. 由于同一数据在数据库里肯定是一行存储,因此会有大量线程来竞争InnoDB行锁,而并发度越高等待线程会越多,TPS会下降,响应事件RT会上升,数据库的吞吐量就会严重手影响。这就会发生单个热点商品影响整个数据库性能,导致0.01%商品影响99.99的商品的售卖。


解决并发锁的问题


  1. 乐观锁/悲观锁
  • 悲观锁:可能会造成大量线程抢锁等待,结果可能会瞬间增大响应时间,造成系统连接数耗尽。
  • 乐观锁:根据版本号的思路,可能会操作操作失败次数增多,需要上层业务重试,或者交给用户重试。
select * from tab1 where id = ?
udpate tab1 set col1 = ? where id = ? and version = ?


缺点:在高并发下可能更新失败,所以要通过重试来提高更新效率。


  1. FIFO队列
  • 排队:并行强制改成串行,单机内存队列,如果生产远高于生产可能造成内存爆掉。即使内存没问题,如果消费过慢用户响应时间也会长。
  1. redis watch
  • 如果可以把数据放到内存数据库中,可以考虑redis watch机制,采用乐观锁方式更新。
WATCH mykey
   val = GET mykey
   val = val + 1
   MULTI
   SET mykey $val
   EXEC


6. 高可用建设

640.png


参考:

  1. 极客时间-如何设计一个秒杀系统 https://time.geekbang.org/column/127
  2. MySQL的并发更新 https://www.cnblogs.com/tao_/p/9537666.html
相关文章
|
5月前
|
安全 定位技术 UED
潮玩宇宙大逃杀游戏系统开发规则详细/成熟技术/案例设计
潮玩宇宙大逃杀开发涵盖概念设计、场景地图构建、角色装备规划、大逃杀机制、多人对战、资源管理、进度排名、UI/UX、防作弊与安全,及持续更新维护。需调整细节满足项目需求,强调团队合作、测试优化以保稳定性和趣味性。
2020考研公共课_基础精讲课_管理类联考综合能力 联考逻辑(读书笔记)
2020考研公共课_基础精讲课_管理类联考综合能力 联考逻辑(读书笔记)
|
机器学习/深度学习 存储 自然语言处理
ChatGPT的各项超能力从哪儿来?万字拆解追溯技术路线图来了!(2)
ChatGPT的各项超能力从哪儿来?万字拆解追溯技术路线图来了!
183 0
|
机器学习/深度学习 存储 人工智能
ChatGPT的各项超能力从哪儿来?万字拆解追溯技术路线图来了!(1)
ChatGPT的各项超能力从哪儿来?万字拆解追溯技术路线图来了!
188 0
|
存储 数据可视化 程序员
选择文库系统的时候需要重点注意和对比哪些东西?
本人程序员出身,接近15年的代码经验,对互联网产品和运营也一直在实践和研究,尤其是对文库产品有着深度理解,因为我自己也一直在运营文库项目。下面是我站在一个普通站长角度给出的一些经验,如果你也想做一个文库网站或文库平台,需要选择一套文库系统产品,请从下面几点出发去做对比,最终做出正确选择。
选择文库系统的时候需要重点注意和对比哪些东西?
|
JSON API 定位技术
商圈库_思路梳理 | 学习笔记
快速学习商圈库_思路梳理。
商圈库_思路梳理 | 学习笔记
|
自然语言处理
如何做竞品分析?
要做竞品分析,首先得明确,什么类型的产品是竞品?
344 0
如何做竞品分析?
|
存储 缓存 关系型数据库
「绝密档案」“爆料”完整秒杀架构的设计到技术关键点的“八卦追踪”
「绝密档案」“爆料”完整秒杀架构的设计到技术关键点的“八卦追踪”
186 0
|
canal 存储 缓存
小林求职记(三)一上来就围绕电商系统层层提问,我太难了....
小林求职记(三)一上来就围绕电商系统层层提问,我太难了....
小林求职记(三)一上来就围绕电商系统层层提问,我太难了....
|
数据可视化 数据挖掘 大数据
以系统化视角反观产品运营,解读提升用户转化的“四部曲”
正常的活动运营通常会围绕公司经营目标,针对不同性质、不同类型的活动开展工作。这样的活动一般会分四个阶段:活动准备、活动策划、活动执行与活动复盘阶段。
以系统化视角反观产品运营,解读提升用户转化的“四部曲”