简述
本篇文章来自于一次内部秒杀系统培训,我将根据架构师的思路从方法论到具体实现进行总结,从业务的拆分到优化思路
方法论
秒杀服务拆分
使用AKF扩展模型进行拆分
业务模型分析
- 秒杀系统涉及的几个领域对象如下:
秒杀(促销中心)—顾客(用户中心)-- 商品(商品中心) – 库存中心
我们要根据业务场景进行领域场景划分 - 划分好领域后,我们要杜绝网状依赖
- 主要瓶颈来自数据库
这里有一组对比的数据:mysql 4G 1000 QPS redis 10W QPS 可见这个性能差距是挺大的 - 服务边界确定
- 秒杀中台关系
优化思路:
前端:
- 页面静态化,也就是我们常说的前后端分离,减少对后端服务的调用
使用的基本方法是:vue.js react.js 这一类的基本前端框架 - 资源静态化,将一些资源访问后,把一些数据保存到redis中,直接访问缓存而不是走后端接口;合并相关css/js 资源,减少并发时流量的压力,可以这样理解 100k的文件,在超高流量的情况下就超过网卡或者宽带限制了,所以这个还是非常必要的。js/css合并(Tengine)减少请求次数
- 多域名加载。对于同一个域名,一个浏览器只会开2-5个并发线程去请求数据,可以通过多域名的方式突破这个加载限制。
- 使用CDN加速,让客户能直接访问到最近的机器资源
后端
-
使用分布式缓存
将促销应用频繁从关系数据库读取的信息预写入分布式缓存,应用读取此类数据直接从缓存中读取,大幅度降底数据库负载,解决关系数据库并发读性能瓶颈问题,活动开始前,将活动进行时所有所需数据加载到分布式缓存(如redis),例如:用户活动参与资格、商品基本信息及库存和活动信息规则等,应用读取时直接从分布式缓存获取
分析:
关系数据库处理能力有性能瓶颈,4C8G配置服务器读处理能力一般2000-3000QPS左右,若使用相同配置的Redis读能力一般10WQPS+,将数据读操作降级到缓存读操作,大幅度提升QPS. -
使用本地缓存(在极致性的场景下才使用)
应用将静态类数据比如商品详情等不变数据第一次从分布式缓存读取后,将此类数据写入到本地缓存,后续从本地缓存直接读取,减少对分布式缓存的访问次数,大幅度提高并发能力
将库存判断标志位写入本地缓存,在库存为0后本地缓存标志为false,后续请求无须执行其它规则判定逻辑,也无须再执行Redis预减库存操作,可直接用户提示返回库存不足结果,大幅度提升应用请求处理能力
-
使用MQ将同步操作变成异步操作
促销活动瞬时会有大量用户涌入,所以在活动一开始会有很高的瞬间峰值。高峰值流量是压垮系统很重要的原因,所以如何把瞬间的高流量变成一段时间平稳的流量也是设计促销活动很重要的思路。比如下单操作,下单整个业务链一般包含生成业务订单生成、冻结库存、写收货地址、写发票信息等,需要对数据库进行多次写的操作,活动开始后下单操作势必成为高并发瓶颈,导致RT过高,用户体验效果极度不好,削峰的常用的方法有利用消息中间件(MQ)等。
-
使用缓存预减的方式进行判断是否有库存,然后再更新数据库,只有成功更新数据库才为购买成功。
数据的切分
- 水平分表
- 垂直分表,把无关的字段分离出去,表尽量的小
- 库表散列,可使用斐波那契数列散列法让散列更加的均匀。
防刷
- 使用验证码削峰
- 隐藏下单请求,类型令牌池的想法
- 本地缓存
库存扣减策略
- 使用分布式缓存