秒杀系统设计总结

系统越往下游走, 并发能力越差, 锁冲突越严重. 关键是将请求尽量拦截在上游

Posted on November 4, 2016

主要策略

  1. 热点隔离

    • 业务隔离
    • 系统隔离
    • 数据隔离
  2. 将请求尽量拦截在上游

    系统越往下游走, 并发能力越差, 锁冲突越严重

    数据分层校验

  3. 动静分离

  4. 充分利用缓存

    考虑在秒杀前后的时间, 其实秒杀也是读多写少的场景

  5. 削峰

    增加验证码, 答题秒杀机制: 拉长并稀释波峰, 同时有防秒杀器作用

    采用队列

  6. 防黄牛, 秒杀器


服务分层

  • 客户端/浏览器/app
  • 应用层/view层/站点
  • 服务层
  • 数据层

秒杀过程

  • 准备阶段:

    秒杀之前, 用户等等秒杀.

  • 秒杀阶段:

    用户参与秒杀.


分层优化

客户端

  • 用户可能反复刷新/点击/查询, 特别是在出现网络慢,并发高, 系统性能下降的场景. 可能增加80%的请求, 然后恶性循环

    优化策略: 拦截请求, 限制流量

    • 产品层面: 点击按钮后将按钮置灰
    • JS层面: 限制在X秒内只能提交一次
    • 准备阶段的页面放CDN, 基本全是读请求
  • 页面开始前不可购买, 防止黄牛用url直接购买

    优化策略:

    • url动态化, url需要校验参数, 在秒杀开始后参数由服务端动态生成
    • 如何点亮点击按钮: 通过动态js/ajax, 去后端获取开抢状态, 并获得必要的随机校验参数
  • 倒计时

    时钟请求逻辑简单, 速度快, 问题不大.

    需要注意多个服务器的时钟同步问题.

  • 动静分离

    秒杀的动态数据和普通的详情页面的动态数据相比更少

    整页缓存到CDN, 异步刷新动态元素, 如倒计时, 开始抢购按钮等

应用层

  • 黄牛写代码刷接口

    优化策略: 拦截请求, 限制到服务层的流量

    按照一定逻辑做页面缓存, 如userid, 商品id, 用户ip

    结合X秒内同一模型返回相同缓存页面

  • 高级黄牛刷接口, 持有大量肉鸡, 用户id等

    • 检测IP请求频率
    • 验证码
    • 用户行为分析, 活跃度分析, 设置秒杀门槛
  • 时钟同步

    对于定时秒杀, 查询接口的后端服务, 要注意时钟同步问题

服务层

  • 优化策略: 拦截请求, 限制到数据层的流量

    • 对于写请求, 用请求队列. 因为秒杀库存有限, 没必要全部打到数据层

      依赖队列写对用户来说是异步的, 需要在产品上进行一定的设计补偿(比如提升稍后在个人中心查看秒杀结果)

    • 对于读请求, 采用分布式缓存, 如redis集群, 可以方便的横向读扩展

  • 异常处理

    • 如果发生雪崩, 需要注意预热
    • 过载保护, 如果检测到系统满负载状态,拒绝请求也是一种保护措施

数据层

如何解决超卖问题?

  • 悲观锁:

    高并发时, 资源消耗巨大

  • 乐观锁:

    有一定cpu开销

    update item_stocks set
    stock = #new_stock#
    where item_id = #item_id# and stock = #old_stock#
    

    redis WATCH 也是乐观锁的实现

  • 尝试减库存 (mysql update满足一致性要求)

    update item_stocks set
    stock = stock-#count#
    where item_id = #item_id# and stock >= #count#
    
  • 先入先出队列更新

    高并发时队列撑爆


业务层优化

  • (热点隔离) 业务隔离, 秒杀商品单独报名, 提前知道热点

  • 点击按钮后将按钮置灰

  • 分时分段销售

  • 数据粒度的优化

    对于余票查询这个业务,票剩了58张,还是26张,你真的关注么,其实我们只关心有票和无票?流量大的时候,做一个粗粒度的“有票”“无票”缓存即可

  • 秒杀页面设计要更简洁

    用户更关心快速的刷新和下单, 页面应该突出秒杀, 减少其他不必要元素, 减少页面大小

    下单表单也应该尽量简洁, 地址使用默认地址, 没有甚至可以不填, 下单成功再补


部署

  • 系统隔离

    独立部署, 避免对现有系统的冲击

    域名, 服务器, 独立部署

  • 数据隔离

    mysql, cache等等需要隔离存放热数据, 目的是不想0.01%的数据影响另外99.99%

  • 应对网络流量的增加, 将秒杀页面(读)部署到CDN


其他

  • 减库存方式:

    • 拍下减库存: 用户体验更好, 但是超卖的概率大

      需要超时回仓, 抢票的启示: 开动秒杀后,45分钟之后再试试看,说不定又有票哟~

    • 付款减库存

  • 其他防黄牛, 秒杀器的方案

    • 验证码
    • 通过购买记录, 访问记录识别
    • IP请求频率
  • 站点层或者服务层处理后台失败的话,重放还是丢弃

    架构设计原则之一是“fail fast”。对用户而言, 有较高失败率的秒杀, 返回失败是合理的


参考链接