商品秒杀在业务运营中是一个很常见的营销手段,运营方通常拿出少量的爆款商品,以很低的价格(1元秒杀iphone12),进行秒杀售卖,加上秒杀前的广告宣传,如此操作,势必短时间内会吸引大量用户过来秒杀,这类系统一般有如下特点:
- 高并发
- 请求量远远大于库存量
那如何保证秒杀业务的正常进行呢?作为研发人员,在设计之初就要考虑好系统的稳定性,可靠性,能不能支撑住大流量请求?如何防止库存超卖?如何防止恶意外挂请求?如何避免其他服务受到牵连?
秒杀系统基本设计原则
- 层层拦截,尽量拦截流量在上游
- 尽量放入少量请求去扣减库存,多者无益,(10个库存,放入10000个请求,没有意义的)
- 充分利用缓存
- 利用缓存读多写少的特性,来扛住大流量
- 利用异步消息队列
- 削峰填谷
秒杀系统设计七要素
-
限流
秒杀活动中,接口的请求量会是平时的数百倍甚至数千倍,从而有可能导致接口不可用,并引发连锁反应导致整个系统崩溃,甚至有可能会影响到其它服务。不能放入大量请求到服务端,需考虑限流(具体可参见:blog.csdn.net/qq_26977063… )
- Nginx限流
- 应用限流( tomcat 限流)
- API限流
- 分布式限流组件 sentinel ,OpenResty
-
削锋
- 排队
- 基于消息
- 提交后,搞一个倒计时,排队提交请求
- 答题
- 复杂验证码+时间控制
- 提交答案,缓存2-10s出结果,避免重复请求,参考12306购票系统
- 分层过滤
- CDN->前台读系统(商品详情系统)->后台写系统(交易系统)->DB
- 每层设置一定过滤条件
- 排队
-
异步
- 消息队列
-
缓存
- 分布式缓存
- 分布式锁
-
可扩展
- 加机器+平滑扩容
-
独立部署
- 目的:解耦合,防止“城门失火,殃及池鱼!”
-
压测
-
知晓系统性能指标,实时监控+告警
-
明确系统瓶颈,及时平滑扩容
-
秒杀系统常见解决方案
-
前端
-
页面静态化+CDN
-
动静分离
-
静态数据缓存
-
用户浏览器
-
CDN
-
cache
-
-
-
动态数据
- 异步JS请求
- 二级缓存
- CDN
- 物理机部署
-
热点数据提前缓存
- 基于访问埋点,提前找出热点商品,设置LRU策略
- 业务上选,如提交报名秒杀商品
- 二八原则
-
-
页面防重复提交
- 提交即置灰
- JS层面,限制用户在x秒之内只能提交一次请求 如此限流,80%流量已拦。
-
用户限流
- 同一时间,同一ip,限制提交次数
-
活动链接参数化
- 防止URL提前泄露
- 基于时间戳做加密链接,时间到了,才可以访问
-
-
后端
-
网关层
- 限制UID访问频率
- IP
- 接口限流
- 同一个uid,限制访问频度,做页面缓存,x秒内到达站点层的请求,均返回同一页面
- 同一个item的查询,例如手机车次,做页面缓存,x秒内到达站点层的请求,均返回同一页面 如此限流,又有99%的流量会被拦截在站点层
- 限制UID访问频率
-
服务层
-
秒杀信息缓存,库存缓存
- 基础信息提前缓存起来,避免直接查询DB
-
用户合法性校验
- 资格校验 ,利用规则剔除不符合用户,如VIP会员可参与
- 考虑下游接口性能,防止拖垮下游
-
秒杀结束,缓存标记
- 结束标签缓存起来,秒杀完成后,后续请求直接返回秒杀失败
-
预冻结库存,库存<=0,秒杀结束,置为结束
- 库存扣减,保证原子操作(decr )
- lua脚本
-
判断是否秒到,秒到则结束
-
预秒杀到的UID,通过消息队列去排队下单
-
真正秒杀开始:校验商品,库存,用户是否秒到,加锁去下单
-
优化
- 秒杀接口隐藏,避免直接暴露,防外挂
- 用户点击秒杀按钮的时候,根据用户id生成唯一的加密串存入缓存并返回给客户端,然后客户端再次请求的时候带着加密串过来,后端进行校验是否合法,若不合法,直接返回请求非法;
- token机制
-
UID
-
client info
-
时间戳
-
- 秒杀接口隐藏,避免直接暴露,防外挂
-
-
DB层(脆弱)
- 秒杀库存表(乐观锁)
- version
- stock > 0
- 秒杀订单表(uId,商品Id,联合唯一索引)
-
-
部署
- 秒杀业务单独部署一套
- 避免其他业务受影响
- 业务降级
- 参考:双11退款业务降级 不可用
- 秒杀业务单独部署一套
以上是常见秒杀系统设计的基本思路,具体使用可以结合具体业务,流量大小来选取不同的解决方案。