设计原因:
为什么要针对秒杀设计一个完善的方案?因为系统可能会因为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等待发生。
如何优化系统:
减少编码:java编码速度慢,涉及字符串操作(输入输出操作、I/O操作)比较消耗CPU资源。原因是磁盘、网络IO都需要将字符串转为字节,这个转换必须查表编码。可通过(OutputStream()直接进行流输出),可提高30%.
减少序列化:序列化与编码同时发生,所以需要减少。尽量减少RPC,将关联性强的应用服务合并。
Java极致优化:对大流量Web系统做静态化改造;直接使用Servlet,绕过框架多余处理逻辑;直接输出流数据。
并发读优化:秒杀系统单机缓存。不要求读一致性,但是写数据的时候要求强一致性。
秒杀系统设计的核心逻辑:
秒杀系统最重要要求是 "不要超卖",关键在于减库存。
减库存方式(三种):
下单减库存:一定不会出现超卖情况,但是有些人下单完不付款会影响其他人。
付款减库款:付款减库存,可能会因为并发高导致付款时已经卖光,付不了款。
预扣库存:最常用,如下单后扣库存,保留十分钟,在十分钟内未付款就不保留。如果付款时发现库存不足则不允许付款。
减库存存在的问题:在下单减库存、预扣库存的情况下,有竞争对手恶意多账号下单导致库存降为0,那商品就无法正常卖。
解决办法:指定反作弊措施,如给经常下单不买的用户打标识、设置最大购买书、设置重复下单不付款操作数。
秒杀减库存极致优化:
秒杀商品减库存放缓存如redis。给热点商品提供独立的缓存层、DB层集群。使用排队如应用层排队、数据库层排队(阿里针对mysql的innodb做了补丁程序patch可对单行记录做并发排队)解决数据库并发锁问题。
设计Plan B兜底方案:
再牛逼的系统也会问题,比如超大流量导致宕机,出现最坏情况,所以需要设计Plan对应高可用性。
各阶段处理操作:
架构阶段:考虑拓展性和容错性,避免系统出现单点问题。
编码阶段:保证代码健壮性,合理设置超时退出机制,对于异常捕获后需要一个默认处理。
测试阶段:保证最坏情况下,也有相应处理流程。
发布阶段:需要有备份用于回滚。
运行阶段:系统监控和报警,如cat监控系统。
故障发生:及时止损,如下架标错价商品。
运行阶段详细处理:
降级:限制或关闭某些非核心功能,留给核心功能。如展示成功记录由30条变成5条。
限流:设置一个QPS阈值,达到则排队或丢弃。
拒绝:当连接数过大,cpu负载达到90%,就拒绝请求。
读自:许令波-《如何设计一个秒杀系统》
原文地址:https://www.cnblogs.com/java188/p/12123721.html