前言
补充知识:(对应的Redis以及QPS可看我之前的文章)
秒杀系统的架构设计在笔试面试都是常考题型
要想达到秒杀系统的高性能、高可用以及一致性,对应的设计如下
1. 高性能
1.1 动静分离
秒杀过程中,页面不用每次都过分刷新再来秒杀
优化:对应将其整个页面的资源整体静态改造,通过静态缓存存储当前页面(对应网站页面将其静态资源缓存,动态资源静态化缓存),刷新的时候将其页面展示给用户。
缓存数据存放?
对应如何缓存,有以下策略。
缓存位置 | 大致情况 |
---|---|
客户端 浏览器 | 秒杀系统下的高并发,如果浏览器不主动刷新页面会有错误 |
服务端 服务器 | 将其大量连接动态计算,本身HTTP解析慢,容易占用CPU资源 |
CDN(推荐) | 将其静态的数据都存储在CDN中(本身擅长处理并发的静态文件请求) |
本身CDN也是一大知识点,对应CDN如何设计合理(保证不失效,命中率高),可通过CDN的二级缓存(容量偏大可更好解决失效以及命中率)
整体页面如何刷新显示?
对应将其页面整体刷新有两种方式
方式 | 简介 | 大致情况 |
---|---|---|
ESI(Edge Side Includes) | 主要在常见的HTTP代理(反向代理、负载均衡、缓存服务器、代理服务器)中使用 | 代理服务器请求动态数据放到静态页面中 |
CSI(Client Side Include) | 简短的JavaScript函数或例程,包含在外部JavaScript文件中 | 代理服务器只返回静态,异步js请求动态数据 |
1.2 热数据处理
所谓的热数据处理主要是头条消息、微博热搜、淘宝热点商品等(只能异步)
海量数据下的TOP N处理(对应的文章可看我之前的文章:海量数据处理的高频面试题分析
),成为热数据,加缓存提高命中率,防止击穿
以上的热数据是事中事后的,如何处理事前的热数据呢(节假日这种情况可增加对应热数据的缓存):
- Nginx代理服务器访问URL对应收集数据缓存:Nginx从入门到精通(全),Nginx配置静态网页访问(图文界面)
- 通过中间件的订阅分发到各链路系统来达到限流缓存等:java中常见的限流算法详细解析
一般系统处理这种热数据的时候尽量跟业务逻辑层、上下游层面解耦化,防止大量影响(不过架构过多不好维护,尽量一个架构,通过区分不同架构、数据、用户、接口)
2. 高可用
为了达到高可用,对应流量进行削峰(mq系列)
关于mq系列,只有kafka文章(后续补充mq系列文章):kafka框架从入门到精通(全)
将其流量削峰,上中游对应层面进行削峰
- 上游:
延缓请求(对应在用户层面增加一些验证请求来减缓流量)
mq消息队列、线程池加锁、nginx或者openresty限流 - 中游:可通过代码层面的限流:java中常见的限流算法详细解析
- 下游:数据库层面(建议限流靠近上游应用层,此方案不推荐)
3. 高一致
一个缓存失效导致整个Redis击穿或者是整体失效导致Redis雪崩
秒杀系统层面一般就是购物的时候下订单
可分为下单、付款、预扣等方式来减库存达到一致(代码逻辑先理清)
减库存方式 | 优势 | 劣势 |
---|---|---|
下单 | 不会出现已下单无法支付(直接操控数据库事务达到一致性) | 下了单不支付,导致其他人用不上 |
付款 | 避免不支付的现象 | 秒杀促销用户体验差,付了钱不一定是你的货物 |
预扣(将其以上两种方式结合),增加下单有效付款时间,超过时间订单释放。但是分阶段会有漏洞可循
实际应用中,为了达到超卖以及秒杀,有如下措施
- 恶意下单不支付(多次不支付的用户拉入黑名单或者不减库存)
- 避免超卖:加货、通过数据库的事务一致性、数据库字段设置为无符号(通过数据库下游或者事务的原子性,这样不会出现负数),甚至可以放入redis的缓存来管理
对应数据库层面
业务 | 处理方式 |
---|---|
高并发读 | 分布式缓存或者LocalCache本地缓存。即使读出脏数据,在下单的时候在进行校验(写链路的时候在进行校验) |
高并发写 | 上游缓存加入分布式锁,下游增加redis数据库 |