0%

架构设计 秒杀系统设计方案

简易描述了秒杀系统的设计思路。

方案背景

秒杀的业务场景

库存固定且少量,所有人会在集中的时间读写这些数据,系统在短时间内会接收到大量的请求。另外比较重要的点是,秒杀系统在售卖时不可超卖。

系统需要解决的主要问题

  1. 友好的用户体验(用户不接受破窗体验,如系统超时、404 等)
  2. 瞬时高并发流量的挑战(木桶短板理论,系统的瓶颈往往都在 DB)
  3. 竞争的资源有限(数据库锁等冲突严重)
  4. 正确的交易状态(竞争状态下不发生超卖、重买等问题)

业务模型分类

直筒型业务模型

直筒型业务模型指的是用户请求 1:1 透传到 Database 中。

漏斗型业务模型

漏斗型业务模型指的是用户的请求从 Client 到 Persistent Layer 层层递减,递减的数量级根据业务来定。

这个模型也揭示了高并发系统的基础:

  • 前端保护后端
  • 及早发现,及早拒绝
  • Fast Fail

因此我们也可以推断出相应的系统优化策略:

  • 将请求尽力拦截在上游
  • 充分利用缓存解决高并发下的读取功能
  • 批量入库提高物理机瓶颈(REDIS 缓存请求队列与 MYSQL 批量入库结合)
  • 热点隔离
    • 业务隔离(如 12306 分时段售票,将热点数据分散处理,降低系统负载压力)
    • 系统隔离(实现系统的软硬件隔离)
    • 数据隔离(启用单独的 Cache、DB 存放热点数据)

可以由上至下四个部分对上述高并发系统基础进行具体实现:

产品策略

  • 轻重逻辑分离:以秒杀为例,将交易和支付逻辑分离
    • 交易是比较轻的操作,成功扣减库存即算作成功
    • 支付是比较重的操作,需要涉及到事务操作
  • 用户分流:以整点秒杀活动为例,在 1 分钟内,陆续对用户放开入口,将所有用户请求打散在 60s 内,请求就可以降一个数量级
  • 页面简化:在秒杀开始的时候,需要简化页面展示,该时刻只保留和秒杀相关的功能。例如,秒杀开始的时候,页面可以不展示推荐的商品。

客户端

  • 重试策略:如果用户秒杀失败后频繁重试,会加剧后端的雪崩。根据后端返回码的约定,有两种重试思路。
    • 不允许重试错误:不允许重试。同时 UI 和文案需要提示。
    • 可重试错误:需要策略重试,例如二进制退避法。同时 UI 和文案需要提示。
  • UI 和文案:秒杀开始前后,用户的所有异常都需要有精心设计的 UI 和文案提示。
    • 例如:[当前活动太火爆,请稍后再重试][你的货物堵在路上,请稍后查看]
  • 前端随机丢弃请求:它可以作为降级方案,当用户流量远远大于系统容量时,人工下发随机丢弃标记,用户本地客户端开始随机丢弃请求。

接入层

  • 所有请求需要鉴权,校验合法身份
    • 如果是长链接的服务,鉴权粒度可以在 session 级别。
    • 如果是短链接业务,需要应对这种高并发流量,例如 Cache 等。
  • 根据后端系统容量,需要一个全局的限流功能,通常有两种做法:
    • 设置好 N 后,动态获取机器部署情况 M,然后下发单机限流值 N/M。要求请求均匀访问,部署机器统一。
    • 维护全局 Key,以时间戳建 Key。 有热 Key问题,可以通过增加更细粒度的 Key 或者定时更新 Key 的方法。
  • 对于单用户/单 IP 需要频控,主要是防黑产和恶意用户(可能会用到令牌桶等功能)。
  • 如果秒杀是有条件的,例如需要完成任务解锁资格,对于获得资格的步骤,可以进行安全扫描,识别出黑产和恶意用户。

逻辑层

  • 逻辑层首先应该进入校验逻辑,例如参数的合法性,是否有资格,如果失败的用户,快速返回,避免请求洞穿到db。
  • 异步补单,对于已经扣除秒杀资格的用户,如果发货失败后,通常的两种做法是:
    • 事务回滚:回滚本次操作,提示用户重试。这个代价特别大,而且用户重试和前面的重试策略结合的话,用户体验也不大流畅。
    • 异步重做:记录本次用户的 log, 提示用户 [稍后查看,正在发货中],后台在峰值过后,启动异步补单。需要服务支持幂等。
  • 对于发货的库存,需要处理热 Key。通常的做法是,维护多个 Key,每个用户固定去某个查询库存。对于大量抢红包的场景,可以提前分配。

存储层

对于业务模型而言,对于 DB 的要求需要保证几个原则:

  • 可靠性
    • 主备:主备能互相切换,一般要求在同城跨机房。
    • 异地容灾:当地异常,数据能恢复,异地能选主。
    • 数据需要持久化到磁盘,或者更冷的设备。
  • 一致性
    • 对于秒杀而言,需要严格的一致性,一般要求主备严格的一致。

方案实践(微视集卡瓜分红包)

微视集卡瓜分红包系统,用户体验流程如下:

架构图

瓜分降级预案

为了做好瓜分时刻的高并发,对整个系统需要保证两个重要的事情:

  • 全链路梳理,包括调用链的合理性和时延设置
  • 降级服务预案分析,提升系统的鲁棒性

如下图所示,是针对瓜分全链路调用分析如下图,需要特别说明的几点:

  • 时延很重要,需要全链路分析。不但可以提高吞吐量,而且可以快速暴露系统的瓶颈。
  • 峰值时刻,补单逻辑需要关闭,避免加剧雪崩。

降级预案大概如下:

  • 一级预案,瓜分时刻前后 5 分钟自动进入:

    • 入口处 1 分钟内陆续放开入口倒计时,未登录用户不弹入口
    • 主会场排队,以进入主会场 100w QPS 为例,超过了进入排队,由接入层频控控制
    • 拉取资格接口排队,拉取资格接口 100w QPS,超过了进入排队,由接入层频控控制
    • 抢红包排队,抢红包 100w QPS,超过了进入排队,由接入层频控控制
    • 红包到账排队,如果资格扣除成功,现金发放失败,进入排队,24 小时内到账。异步补单
    • 入口处调用后端非关键 rpc:ParticipateStatus,手动关闭
    • 异步补单逻辑关闭。
  • 二级预案,后端随机丢请求,接入层频控失效或者下游服务过载,手动开启

  • 三级预案,前端随机丢请求,后端服务过载或者宕机进入,手动开启

用户体验流程