E. 业务场景 --- 秒杀系统
程序员文章站
2022-04-22 08:05:11
...
E. 业务场景 --- 秒杀系统
概要
核心问题
并发读
并发写
秒杀的整体架构可以概括为“稳、准、快”几个关键字
所谓“稳”,就是整个系统架构要满足高可用
就是“准”,就是秒杀 10 台 iPhone,那就只能成交10 台,多一台少一台都不行。
最后再看“快”,“快”其实很好理解,它就是说系统的性能要足够高,否则你怎么支撑这么大的流量呢?
架构设计原则
数据要尽量少
首先是指用户请求的数据能少就少
其次,“数据要尽量少”还要求系统依赖的数据能少就少
请求数要尽量少
路径要尽量短
依赖要尽量少
系统降级减少依赖,我们可以给系统进行分级,比如 0 级系统、1 级系统、2 级系统、3 级系统
不要有单点
样例概述
第一阶段
把秒杀系统独立出来单独打造一个系统,这样可以有针对性地做优化
在系统部署上也独立做一个机器集群,这样秒杀的大流量就不会影响到正常的商品购买集群的机器负载
将热点数据(如库存数据)单独放到一个缓存系统中,以提高“读性能”
增加秒杀答题,防止有秒杀器抢单
第二阶段
对页面进行彻底的动静分离,使得用户秒杀时不需要刷新整个页面
在服务端对秒杀商品进行本地缓存,不需要再调用依赖系统的后台服务获取数据,甚至不需要去公共的缓存集群中查询数据,这样不仅可以减少系统调用,而且能够避免压垮公共缓存集群
增加系统限流保护,防止最坏情况发生
高性能/高可用
关注点
一点是提高单次请求的效率
一点是减少没必要的请求
要点
动静分离也就是所谓“动态”还是“静态”,并不是说数据本身是否动静,而是数据中是否含有和访问者相关的个性化数据。
静态数据的处理
你应该把静态数据缓存到离用户最近的地方
浏览器
CDN
服务器的Cache
静态化改造就是要直接缓存 HTTP 连接Web 代理服务器根据请求 URL,直接取出对应的 HTTP响应头和响应体然后直接返回,这个响应过程简单得连 HTTP 协议都不用重新组装,甚至连 HTTP 请求头也不需要解析。
让谁来缓存静态数据也很重要
动态内容改造
URL 唯一化
分离浏览者相关的因素浏览者相关的因素包括是否已登录,以及登录身份等,这些相关因素我们可以单独拆分出来,通过动态请求来获取。
分离时间因素。服务端输出的时间也通过动态请求获取。
异步化地域因素。详情页面上与地域相关的因素做成异步方式获取,当然你也可以通过动态请求方式获取,只是这里通过异步获取更合适。
去掉 Cookie
动态数据的处理
ESI 方案(或者 SSI):即在 Web 代理服务器上做动态内容请求,并将请求插入到静态页面中,当用户拿到页面时已经是一个完整的页面了。这种方式对服务端性能有些影响,但是用户体验较好。
CSI 方案。即单独发起一个异步 JavaScript 请求,以向服务端获取动态内容。这种方式服务端性能更佳,但是用户端页面可能会延时,体验稍差。
架构方案
单机部署这种方案是将虚拟机改为实体机,以增大 Cache 的容量,并且采用了一致性 Hash 分组的方式来提升命中率。
优点
没有网络瓶颈,而且能使用大内存;
既能提升命中率,又能减少 Gzip 压缩;
减少 Cache 失效压力,因为采用定时失效方式,例如只缓存 3 秒钟,过期即自动失效;
缺点
一定程度上也造成了 CPU 的浪费,因为单个的 Java 进程很难用完整个实体机的 CPU。
一个实体机上部署了 Java 应用又作为 Cache 来使用,这造成了运维上的高复杂度
统一Cache:分布式缓存
优点
单独一个 Cache 层,可以减少多个应用接入时使用Cache 的成本。这样接入的应用只要维护自己的 Java 系统就好,不需要单独维护 Cache,而只关心如何使用即可。
统一 Cache 的方案更易于维护,如后面加强监控、配置的自动化,只需要一套解决方案就行,统一起来维护升级也比较方便。
可以共享内存,最大化利用内存,不同系统之间的内存可以动态切换,从而能够有效应对各种攻击。
缺点
Cache 层内部交换网络成为瓶颈;
缓存服务器的网卡也会是瓶颈;
机器少风险较大,挂掉一台就会影响很大一部分缓存数据。
CDN
问题
失效问题
命中率问题
发布更新问题
解决方案
靠近访问量比较集中的地区;
离主站相对较远;
节点到主站间的网络比较好,而且稳定;
节点容量比较大,不会占用其他 CDN 太多的资源;
热点数据
什么是热点
热点操作
读请求
写请求
热点数据
静态数据热点:能够提前预测的数据例如,我们可以通过卖家报名的方式提前筛选出来,通过报名系统对这些热点商品进行打标。比如我们分析历史成交记录、用户的购物车记录,来发现哪些商品可能更热门、更好卖
动态数据热点:系统运行过程总产生的热点例如,卖家在抖音上做了广告,然后商品一下就火了,导致它在短时间内被大量购买。
问题
首先,热点请求会大量占用服务器处理资源,虽然这个热点可能只占请求总量的亿分之一,然而却可能抢占 90% 的服务器资源,如果这个热点请求还是没有价值的无效请求,那么对系统资源来说完全是浪费
其次,即使这些热点是有效的请求,我们也要识别出来做针对性的优化,从而用更低的代价来支撑这些热点请求。
发现热点
发现静态热点数据
静态热点数据可以通过商业手段,例如强制让卖家通过报名参加的方式提前把热点商品筛选出来
还可以通过技术手段提前预测,例如对买家每天访问的商品进行大数据计算,然后统计出 TOP N 的商品,我们可以认为这些 TOP N 的商品就是热点商品。
发现动态热点数据
步骤
构建一个异步的系统,它可以收集交易链路上各个环节中的中间件产品的热点 Key
建立一个热点上报和可以按照需求订阅的热点服务的下发规范,主要目的是通过交易链路上各个系统(包括详情、购物车、交易、优惠、库存、物流等)访问的时间差,把上游已经发现的热点透传给下游系统,提前做好保护。
将上游系统收集的热点数据发送到热点服务台,然后下游系统(如交易系统)就会知道哪些商品会被频繁调用,然后做好热点保护
注意事项
这个热点服务后台抓取热点数据日志最好采用异步方式,因为“异步”一方面便于保证通用性,另一方面又不影响业务系统和中间件产品的主流程
热点服务发现和中间件自身的热点保护模块并存,每个中间件和应用还需要保护自己。
热点发现要做到接近实时(3s 内完成热点数据的发现)
处理热点数据
优化:对于热点数据,直接缓存
限制:可以把热点商品限制在一个请求队列里,防止因某些热点商品占用太多的服务器资源,而使其他请求始终得不到服务器的处理资源
隔离
业务隔离
系统隔离
数据隔离
削峰
我们知道服务器的处理资源是恒定的,你用或者不用它的处理能力都是一样的,所以出现峰值的话,很容易导致忙到处理不过来,闲的时候却又没有什么要处理。但是由于要保证服务质量,我们的很多处理资源只能按照忙的时候来预估,而这会导致资源的一个浪费
好处
一是可以让服务端处理变得更加平稳
二是可以节省服务器的资源成本
解决思路
排队
消息队列
线程池
先进先出、先进后出等常用的内存排队算法的实现方式
答题
第一个目的是防止部分买家使用秒杀器在参加秒杀时作弊
第二个目的其实就是延缓请求,起到对请求流量进行削峰的作用,从而让系统能够更好地支持瞬时的流量高峰。
过滤:在不同的层次尽可能地过滤掉无效请求,让“漏斗”最末端的才是有效请求
大部分数据和流量在用户浏览器或者 CDN 上获取,这一层可以拦截大部分数据的读取;
经过第二层(即前台系统)时数据(包括强一致性的数据)尽量得走 Cache,过滤一些无效的请求;
再到第三层后台系统,主要做数据的二次检验,对系统做好保护和限流,这样数据量和请求就进一步减少;
最后在数据层完成数据的强一致性校验
一致性
减库存的方式
下单减库存:但是你要知道,有些人下完单可能并不会付款
付款减库存:有可能出现买家下单后付不了款的情况,因为可能商品已经被其他人买走了
预扣库存:买家下单后,库存为其保留一定的时间(如 10 分钟)
库存超卖
对普通的商品下单数量超过库存数量的情况,可以通过补货来解决;
但是有些卖家完全不允许库存为负数的情况,那只能在买家付款时提示库存不足
库存优化
在缓存减库存
数据库并发锁问题
热点数据隔离
应用层做排队
数据库层做排队
限流降级
上一篇: 从零开始完整搭建 Spring-Boot 项目开发框架的教程
下一篇: 最牛的耗子药