最近学习的一门课程,记录下学习笔记
感兴趣的朋友可以去购买,课程地址:如何设计一个秒杀系统 (geekbang.org)
本文会一直更新修改,因为一天看一点学一点,嘻嘻,:)
开篇词 | 秒杀系统架构设计都有哪些关键点?
从技术角度上看“稳、准、快”,就对应了我们架构上的高可用、一致性和高性能的要求:
- 高性能。 秒杀涉及大量的并发读和并发写,因此支持高并发访问这点非常关键。本专栏将从设计数据的动静分离方案、热点的发现与隔离、请求的削峰与分层过滤、服务端的极致优化这 4 个方面重点介绍。
- 一致性。 秒杀中商品减库存的实现方式同样关键。可想而知,有限数量的商品在同一时刻被很多倍的请求同时来减库存,减库存又分为“拍下减库存”“付款减库存”以及预扣等几种,在大并发更新的过程中都要保证数据的准确性,其难度可想而知。因此,我将用一篇文章来专门讲解如何设计秒杀减库存方案。
- 高可用。 虽然我介绍了很多极致的优化思路,但现实中总难免出现一些我们考虑不到的情况,所以要保证系统的高可用和正确性,我们还要设计一个 PlanB 来兜底,以便在最坏情况发生时仍然能够从容应对。专栏的最后,我将带你思考可以从哪些环节来设计兜底方案。
01 | 设计秒杀系统时应该注意的5个架构原则
秒杀系统本质上就是一个满足大并发、高性能和高可用的分布式系统。
架构原则:“4 要 1 不要”
数据要尽量少、请求数要尽量少、路径要尽量短、依赖要尽量少,以及不要有单点。
- 数据要尽量少。
所谓“数据要尽量少”,首先是指用户请求的数据能少就少。请求的数据包括上传给系统的数据和系统返回给用户的数据(通常就是网页)。减少传输的数据量可以显著减少 CPU 的使用。例如,我们可以简化秒杀页面的大小,去掉不必要的页面装修效果,数据库打交道越少越好,数据越简单、越小则越好,等等。
- 请求数要尽量少。
页面依赖的 CSS/JavaScript、图片,以及 Ajax 请求等等都定义为“额外请求”,这些额外请求应该尽量少。
- 路径要尽量短。
所谓“路径”,就是用户发出请求到返回数据这个过程中,需求经过的中间的节点数。
- 依赖要尽量少
所谓依赖,指的是要完成一次用户请求必须依赖的系统或者服务,这里的依赖指的是强依赖。\
注意,0 级系统要尽量减少对 1 级系统的强依赖,防止重要的系统被不重要的系统拖垮。例如支付系统是 0 级系统,而优惠券是 1 级系统的话,在极端情况下可以把优惠券给降级,防止支付系统被优惠券这个 1 级系统给拖垮。
- 不要有单点
系统中的单点可以说是系统架构上的一个大忌,因为单点意味着没有备份,风险不可控,我们设计分布式系统最重要的原则就是“消除单点”。\
那如何避免单点呢?我认为关键点是避免将服务的状态和机器绑定,即把服务无状态化,这样服务就可以在机器中随意移动。
架构是一种平衡的艺术,而最好的架构一旦脱离了它所适应的场景,一切都将是空谈。
不同场景下的不同架构案例
请求量低于1w/s
如果你想快速搭建一个简单的秒杀系统,只需要把你的商品购买页面增加一个“定时上架”功能,仅在秒杀开始时才让用户看到购买按钮,当商品的库存卖完了也就结束了。这就是当时第一个版本的秒杀系统实现方式。
请求量1w/s~10w/s
- 把秒杀系统独立出来单独打造一个系统,这样可以有针对性地做优化,例如这个独立出来的系统就减少了店铺装修的功能,减少了页面的复杂度;
- 在系统部署上也独立做一个机器集群,这样秒杀的大流量就不会影响到正常的商品购买集群的机器负载;
- 将热点数据(如库存数据)单独放到一个缓存系统中,以提高“读性能”;
- 增加秒杀答题,防止有秒杀器抢单。
请求量超过100w/s
- 对页面进行彻底的动静分离,使得用户秒杀时不需要刷新整个页面,而只需要点击抢宝按钮,借此把页面刷新的数据降到最少;
- 在服务端对秒杀商品进行本地缓存,不需要再调用依赖系统的后台服务获取数据,甚至不需要去公共的缓存集群中查询数据,这样不仅可以减少系统调用,而且能够避免压垮公共缓存集群。
- 增加系统限流保护,防止最坏情况发生。
02 | 如何才能做好动静分离?有哪些方案可选?
何为动静数据
动态数据”和“静态数据”的主要区别就是看页面中输出的数据是否和 URL、浏览者、时间、地域相关,以及是否含有 Cookie 等私密数据。
静态数据,不能仅仅理解为传统意义上完全存在磁盘上的 HTML 页面,它也可能是经过 Java 系统产生的页面,但是它输出的页面本身不包含上面所说的那些因素。也就是所谓“动态”还是“静态”,并不是说数据本身是否动静,而是数据中是否含有和访问者相关的个性化数据。
页面中“不包含”,指的是“页面的 HTML 源码中不含有”,这一点务必要清楚。
怎样对静态数据做缓存呢?
第一,你应该把静态数据缓存到离用户最近的地方。
第二,静态化改造就是要直接缓存 HTTP 连接。
第三,让谁来缓存静态数据也很重要。不同语言写的 Cache 软件处理缓存数据的效率也各不相同。
如何做动静分离的改造
5 个方面来分离出动态内容:
1.URL 唯一化。
2.分离浏览者相关的因素。
3.分离时间因素。
4.异步化地域因素。
5.去掉 Cookie。
动态内容的处理通常有两种方案:ESI(Edge Side Includes)方案和 CSI(Client Side Include)方案。
动静分离的几种架构方案
实体机单机部署;统一 Cache 层;上 CDN。
实体机单机部署
Hash 分组越少,缓存的命中率肯定就会越高,但短板是也会使单个商品集中在一个分组中,容易导致 Cache 被击穿,所以我们应该适当增加多个相同的分组,来平衡访问热点和命中率的问题。
统一 Cache 层
将 Cache 层单独拿出来统一管理可以减少运维成本,同时也方便接入其他静态化系统。
对 Cache 做 Hash 分组,即一组 Cache 缓存的内容相同,这样能够避免热点数据过度集中导致新的瓶颈产生。
上 CDN
Cache 进一步前移到 CDN 上,因为 CDN 离用户最近,效果会更好。
将商品详情系统放到全国的所有 CDN 节点上是不太现实的,因为存在失效问题、命中率问题以及系统的发布更新问题。
可以选择若干个节点来尝试实施。这样的节点需要满足几个条件:
靠近访问量比较集中的地区;离主站相对较远;节点到主站间的网络比较好,而且稳定;节点容量比较大,不会占用其他 CDN 太多的资源。节点不要太多。
因为在 CDN 上,我们可以做主动失效,而在用户的浏览器里就更不可控,如果用户不主动刷新的话,你很难主动地把消息推送给用户的浏览器。
二级cache是指cdn设置了多级回源机制,就是如果缓存没有命中再到二级缓存中去取,而不是直接回服务端来请求
Cache层是web缓存可以直接缓存http请求,例如varnish