设计一个秒杀系统
1 设计的架构原则
1.1 4要1不要
-
尽量减少请求数量
js,css等额外请求要少 合并请求
尽量减少请求数据
-
尽量减少请求路径
用户发出去请求到返回数据过程中,经过的中间节点数要少
-
请求依赖要尽量少
秒杀系统中的商品信息和用户信息是关键信息,优惠券列表,成交列表弱依赖
-
不要有单点
避免服务状态华
1.2 不同场景的架构
商品购买增加定时上架 -- 秒杀开始,才能看到按钮
单独部署 不和其他业务冲突
热点数据放到缓存
增加秒杀答题,方式秒杀器抢单
页面动静分离
在服务端对秒杀少商品进行缓存
增加系统限流保护
2 动静分离的可选方案
2.1 为什么要考虑动静分离
提高了单次的请求效率
减少了没必要的请求
2.2 怎么区分动静数据
动静数据主要区别就是看页面中输出的数据是否和URL,浏览者,时间,地域相关,以及是否含有Cookie等私密数据。
静态数据不是磁盘上的HTML内容,而是数据中是否有个性化的数据。
2.3 怎么对静态数据缓存
把静态数据缓存到离用户最近的地方,例如 浏览器里,CDN,或者服务端的Cache。
静态化改造就是要直接缓存HTTP连接,WEB代理服务器根据请求URL,直接取出对应的HTTP响应头和响应体直接返回。
在系统那个部分做静态缓存?WEB服务器由于Java语言。
2.4 怎么进行动静分离
怎么分离动态内容:
URL唯一话,每个商品的URL是独一无二的。
分离浏览者相关的因素,包括是否登录,以及登录身份,这些相关因素可以单独拆分出来,通过动态请求来获取。
分离时间因素,服务端输出的时间也应该通过动态请求获取。
异步化地域因素,请求页面上与地域相关的因素做异步式获取。
去掉Cookie。缓存的静态数据中不包含有Cookie.
动态内容处理通用的两种方案:
ESI方案,在Web代理服务器上做动态内容请求,并将请求插入静态页面中,当用户拿到页面的时候已经是一个完整的页面。
CSI方案,单独发起一个异步的JavaScript请求,以向服务端获取动态内容。
2.5 架构方案
2.5.1 实体机单机部署
采用一致性Hash分组的方式来提升命中率,这里将Cache分成若干组,是希望能达到命中率和访问率的平衡。
优点:
- 没有网络瓶颈,而且能使用大内存。
- 既能提升命中率,又能减少Gzip压缩。
- 减少Cache失效压力,因为采用定时失效方式。
缺点:
运维复杂度高
2.5.2 统一Cache层
将单机的Cache统一分离出来,形成一个单独的Cache集群。
优点:
- 可以减少多个应用接入Cache的成本,只需关注自己的Java系统就好。
- 易于维护。
- 共享内存,最大化利用内存。不同系统之间的内存可以动态切换。
缺点:
- Cache层内部交换网络成为瓶颈。
- 缓存服务器的网卡也会成为瓶颈。
- 机器少,风险比较大,挂掉一台会影响很大一部分缓存数据。
2.5.3 上CDN
3 二八原则:有针对的处理好系统的热点数据
3.1 热点?
热点请求会占用大量服务器资源。
热点操作:
热点数据:静态热点数据(提前预测的热点数据,大数据分析热点商品,历史成交记录,用户购物车)+动态热点数据()
3.2 发现热点数据
3.2.1 发现静态热点数据
- 卖家报名的方式,然后给卖家的商品打tag,数据进行预处理,然后缓存数据。
- 分析买家访问的商品等,然后统计出TOP N的商品。
3.2.2 发现动态热点数据
系统内部秒级自动发现热点商品
- 构建一个异步的系统,进行收集交易链路上各个环节中的中间件热点key。如:Nginx,缓存,RPC服务框架等。
- 上游系统把已经发现的热点透传给下游系统,提前做好保护。
3.3 如何处理热点数据
- 优化:缓存热点数据,用队列进行存储,采用LRU淘汰算法。
- 限制:作为一种保护机制,把热点请求限制在一个请求队列里面。防止热点数据占用太多资源。
- 隔离:热点数据隔离出来,不要让1%的请求影响到另外的99%。业务隔离+系统隔离+数据隔离。
4 流量削峰
秒杀这个场景来说,最终抢到商品的人数是固定的,所以100人和10000人发起请求的结果都是一样的,并发度越高,无效请求越多。
4.1 为什么削峰
服务器能处理的资源是恒定的,所以为了服务稳定,我们延缓用户请求的发出,以便减少和过滤掉一些无效的请求。
4.2 削峰的思路
- 排队
把一步操作,变成二步操作,其中增加一步操作用来起到缓冲的作用。
- 利用消息队列,把请求缓冲起来,然后消费端异步处理请求。
- 利用线程池加锁等待。
- 先进先出内存排队算法的实现方式。
- 把请求序列化到文件中,然后顺序的读文件来恢复请求操作。
- 答题
一方面防止买家使用秒杀器在参加秒杀的时候作弊。一方面延缓请求,起到流量削峰的作用。
- 分层过滤
- 大部分数据和流量在用户浏览器或者CDN上获取,这一层拦截大部分数据的读取。
- 前台系统的数据,尽量得走cache,过滤一些无效请求。
- 后台系统,做数据的二次校验,对系统做好保护和限流。
- DB数据层,完成数据的强一致性校验。
基本原则:
- 将动态请求的读数据缓存在Web端,过滤掉无效请求读。
- 对读数据不做强一致性校验,减少因为一致性校验产生瓶颈的问题。
- 对写数据进行基于时间的合理分片,过滤掉过期的失效请求。
- 对写请求做限流保护,将超出系统承载能力的请求过滤掉。
- 对写数据进行强一致性校验,只保留最后有效的数据。
5 提升系统性能
5.1 影响性能的因素
响应时间:
每秒请求数:(Query Per Second)
CPU的执行时间+线程数
线程数=2*cpu+1
线程数=[(线程等待时间+线程CPU时间)/线程CPU时间]*CPU数量
5.2 如何发现瓶颈
CPU诊断工具:Jprofiler+Yourkit工具+jstack定时地打印调用栈,
QPS达到极限时候,CPU的使用率是不是超过95%,如果没有超过,那么CPU还有提升的空间。
5.3 如何优化系统
- 减少编码:
- 减少序列化
- Java极致优化 直接使用Servlet处理请求,直接输出流数据。
- 并发读优化 不变的信息,缓存系统内存里面,变化的信息,采用失效时间,失效之后拉取最新数据。
6 秒杀系统 减库存设计
6.1 减库存的几种方式
- 下单减库存:有些人下单之后不会付款。
- 付款减库存:买家下单之后,不能发起付款。
- 预扣库存:买家下单之后,库存为其保留一定时间,超过这个时间,库存自动释放。在买家付款之前,系统会校验该订单的库存是否保留,如果没有保留,则再次尝试预扣,如果预扣失败,则不允许继续付款。
6.2 数据库一致性
- 应用程序中通过事物来判断,即保证减库存不能为负数,否则就回滚。
- 数据库字段设置为无符号整数,小于0的时候SQL抛出异常。