秒杀系统设计思路笔记

时间:2024-03-21 22:07:56

设计原因:

为什么要针对秒杀设计一个完善的方案?因为系统可能会因为1%的秒杀业务影响其余99%正常业务的运行,所以需要将秒杀系统独立出来。


待解决问题:

主要解决两个问题:并发读、并发写。


整体架构要求:

概括为:"稳、准、快",即对应"高可用、一致性、高性能",其介绍分别如下:

  • 高可用:保证系统的高可用和正确性,设计PlanB进行兜底。

  • 一致性:保证秒杀减库存中的数据一致性。

  • 高性能:涉及大量并发读写,所以需要支持高并发,从动静分离、热点发现与隔离、请求削峰与分层过滤、服务端极致优化来介绍。


五个架构原则(4要1不要):

数据尽量少:包含请求和响应的数据,因为网络传输需要时间。可简化秒杀页面、减少数据库打交道。

请求数尽量少:浏览器渲染页面包含额外请求,比如以来了css、js、图片等定义为额外请求,这些请求尽可能少,若请求域名不一致,还涉及DNS解析,耗时更久,可采用合并方法如:http://xxx.com/tm/modes??module-preview/index.xtpl.js,module-jhs/index.xtpl.js ,这个在服务器依旧是单独存放,只是提供了一个组件解析这个url。比如以下是淘宝的一个静态资源链接:

秒杀系统设计思路笔记

路径尽量短:这里的路径表示节点。每个节点都会产生新的socket连接,一个节点的可用性是99.9%,经过五个节点后就是五次方为99.5%。方法可以是服务内部RPC调用变为jvm内部调用。

依赖尽量少:将服务分级,比如将支付服务作为0级,优惠券是1级,要避免0级系统被1级系统拖垮。

不要有单点:单点意味着没有备份。将服务无状态化,即避免服务与机器绑定。可设置配置中心映射。

:数据、请求数、路径、依赖、单点。但是其实这些原则会产生冲突,如请求数量少中的合并请求会使单次请求的数据量变大,这与数据尽量少违背。所以需要我们做一个平衡。


秒杀系统的大体涉及结构:

秒杀系统设计思路笔记

1 页面彻底动静分离,使得用户秒杀时不需要刷新整个页面,讲底刷新请求书。

2 服务器缓存秒杀商品,直接调用缓存层,无需穿透到数据库层找数据。

3 增加流量限流保护,防止最g坏情况。 


如何动静分离:

把用户请求数据(如HTML)分为"动态数据"和"静态数据"。

 

动态数据与静态数据区别:确定输出的数据是否含访问者个性化数据如 个人信息、cookie等私密数据。

 

区别动静数据的作用:区分了动静数据,就可以将静态数据缓存,提高效率。

 

如何对静态数据做缓存:

1. 将静态数据放在离用户最近的地方,如CDN、用户浏览器、服务端的Cache。

2. 做静态化改造,直接缓存HTTP连接。Web服务器根据请求URL直接取出HTTP响应头、响应体直接返回。

3. 选择谁来缓存静态数据,如可在web服务器层做缓存,屏蔽java层的弱点,不在java层做缓存。

 

如何做动静分离改造:

1. URL唯一化。如每个商品链接为:http://item.xxx/xxxx?id=xxx 来作为缓存的key,用于缓存整个HTTP连接。

2. 分离请求,含用户信息相关、时间、地狱相关,这些都可以通过异步请求独立获取。

3. 服务器返回的信息可去掉cookie。如Vanish可用unset req.http.cookie去掉cookie。

 

动态内容处理方案:

ESI:web代理服务器做动态请求时,将动态内容插入静态页面,然后全部返回。

CSI:异步js请求,性能最佳,但是有一定时延,可能对用户体验不好。

 

动静分离的几种架构方案:

1. 实体机单机部署:将虚拟机运行的Java应用换成实体机

优点:无网络瓶颈、可使用大内存。

          提升命中率,减少Gzip压缩。

          减少cache失效压力,因为采用定时失效,如3分钟失效。

秒杀系统设计思路笔记

2 统一Cache层:即抽离cache出来作为一个独立的集群。可设置二级Cache,放置回原(原服务器)

 

3. 上CDN:可增加二级Cache防止回原(原服务器)。


二八原则(针对性处理热点数据):

为什么要针对性处理:热点数据会大量占用服务资源,0.1%业务会抢占系统90%以上的资源。

 

什么是热点:

    热点分为热点操作和热点数据,其中热点操作是一种优化的方式,将会在秒杀的操作优化讲解。

    热点数据分为动态、静态热点数据如下:

    -静态热点数据即可提前预测数据如数据分析出哪种更热门、一个商品做活动等。

    -动态热点数据不可预测,如抖音广告然后突然火了。

 

如何发现热点数据:

    发现静态热点数据:强制让卖家通过报名方式提前把热点数据筛选出来缓存,但是增加了卖家的工作量,也不够实时。也可以根据每日访问数进行统计,然后缓存TOP N的商品。

 

    发现动态热点数据:抽离出一个中间件用于收集搜索、商品详情、购物车等关键热点业务的点击数据,然后异步记录到日志,然后根据规则判断是否热点数据后缓存在队列中(因为热点数据一般是临时的,所以可采用LRU算法淘汰)。

 


流量削峰怎么做:

为什么要削峰:稳定服务端,节省资源,本质是延缓用户请求发出,减少和过滤无用请求(遵循请求书尽量少原则)。

 

削峰思路:排队、答题、分层过滤

    排队:消息队列缓存大量并发,把原来的一步操作变成两步。虽然违背了增加访问路径原则,但是防止了系统崩溃。

    答题:可防止爬虫等的自动抢购的脚本。延缓请求从之前的1s内延缓到2-10s,对事件进行了分片,减缓服务器压力,如微信的摇一摇、支付宝休一休。也可限制答题时间间隔。

    分层过滤:分层为:CDN->前台读系统(商品详情系统)->后台写系统(交易系统)->DB

  • 大部分数据和流量都在CDN获取,拦截了大部分读的数据。

  • 经过第二层(前台读系统)尽量走Cache。

  • 到第三层(后台写系统),做数据校验、限流,进一步减少数据量和请求。

  • 最后在数据层完成强一致性校验。

 

分层过滤核心思想:各层过滤无效请求,所以必须对数据做分层校验,其校验原则如下:

  • 将动态请求的读数据缓存在浏览器本地,过滤无效数据读。

  • 对读数据不做强一致性校验,较少一致性校验带来的性能问题。

  • 对写数据基于时间的合理分片,过滤过期失效请求。

  • 对写数据做强一致性校验,只保留有效数据。


影响性能的因素及其可优化处:

影响服务端性能的因素:QPS、响应时间(RT)

    计算公式:QPS = (1000ms / 响应时间) * 线程数量,真正影响性能的是CPU执行时间。


    再来分析下线程数是否对QPS的影响:不是线程数越多,QPS越高,因为线程上下文切换有消耗。所以需要合理的设置线程数,一般的计算公式为:线程数 = [(线程等待时间 + 线程CPU时间) / 线程CPU时间] * CPU数量,当然最好的方式是性能测试来确认。

 

如何发现瓶颈:就缓存系统而言,制约的是内存。存储系统的瓶颈是I/O。

 

秒杀系统的大部分瓶颈在CPU(使用JProfiler、YourKit),但不一定是CPU,有可能是其他部分,比如QPS达到极限时,CPU使用率是否超过95%,如果不是则可能是锁限制或过多本地I/O等待发生。

 

如何优化系统:

  1. 减少编码:java编码速度慢,涉及字符串操作(输入输出操作、I/O操作)比较消耗CPU资源。原因是磁盘、网络IO都需要将字符串转为字节,这个转换必须查表编码。可通过(OutputStream()直接进行流输出),可提高30%.

  2. 减少序列化:序列化与编码同时发生,所以需要减少。尽量减少RPC,将关联性强的应用服务合并。

  3. Java极致优化:对大流量Web系统做静态化改造;直接使用Servlet,绕过框架多余处理逻辑;直接输出流数据。

  4. 并发读优化:秒杀系统单机缓存。不要求读一致性,但是写数据的时候要求强一致性。


秒杀系统设计的核心逻辑:

秒杀系统最重要要求是 "不要超卖",关键在于减库存。

 

减库存方式(三种):

  1. 下单减库存:一定不会出现超卖情况,但是有些人下单完不付款会影响其他人。

  2. 付款减库款:付款减库存,可能会因为并发高导致付款时已经卖光,付不了款。

  3. 预扣库存:最常用,如下单后扣库存,保留十分钟,在十分钟内未付款就不保留。如果付款时发现库存不足则不允许付款。

 

减库存存在的问题:在下单减库存、预扣库存的情况下,有竞争对手恶意多账号下单导致库存降为0,那商品就无法正常卖。

    解决办法:指定反作弊措施,如给经常下单不买的用户打标识、设置最大购买书、设置重复下单不付款操作数。

 

秒杀减库存极致优化:

    秒杀商品减库存放缓存如redis。给热点商品提供独立的缓存层、DB层集群。使用排队如应用层排队、数据库层排队(阿里针对mysql的innodb做了补丁程序patch可对单行记录做并发排队)解决数据库并发锁问题。


设计Plan B兜底方案:

再牛逼的系统也会问题,比如超大流量导致宕机,出现最坏情况,所以需要设计Plan对应高可用性。

 

各阶段处理操作:

  1. 架构阶段:考虑拓展性和容错性,避免系统出现单点问题。

  2. 编码阶段:保证代码健壮性,合理设置超时退出机制,对于异常捕获后需要一个默认处理。

  3. 测试阶段:保证最坏情况下,也有相应处理流程。

  4. 发布阶段:需要有备份用于回滚。

  5. 运行阶段:系统监控和报警,如cat监控系统。

  6. 故障发生:及时止损,如下架标错价商品。

 

运行阶段详细处理:

  1. 降级:限制或关闭某些非核心功能,留给核心功能。如展示成功记录由30条变成5条。

  2. 限流:设置一个QPS阈值,达到则排队或丢弃。

  3. 拒绝:当连接数过大,cpu负载达到90%,就拒绝请求。

读自:许令波-《如何设计一个秒杀系统》