秒杀系统我的总结

秒杀系统架构

秒杀系统是一个并发量要求高、负载均衡要求高的、业务场景简单可是逻辑稍微复杂的系统,因此常常会做为面试高级后端开发的面试题。主要考察的就是对问题的拆解、分析、解决,以及架构设计的能力。前端

基础架构

  • 客户端
    • web 浏览器 / app
  • 负载均衡层
    • Nginx
  • web 层
    • 接收 Http 请求
    • 作限流
      • 分布式限流
      • id 限流
  • service 层
    • 库存操做
    • 生成订单
  • 数据存储层
    • mq + mysql

客户端

服务端是一个潜在的考察点,仍是有不少问题须要解决的。有些网上给出的设计方案没有对这块作详细考虑。mysql

  • 客户端限流(在浏览器上行不通)web

    客户端作必定控制来限流(好比几率),防止刷单,减小成功次数,显示在排队,实际上没发网络请求面试

  • 前端展现redis

    秒杀按钮展现要有个定时器,涉及到先后端时钟同步的问题sql

    • 校对时间差后端

      获取服务端时间,客户端时间 - 服务端时间,比较获得差值,用这种方法来同步。然而注意到网络是有开销的,这个开销须要想办法消除。不然这种毫秒级甚至秒级的时间差,会影响到秒杀的公平性。浏览器

      • 发送更轻量级的服务响应
      • 优化代码,使客户端和服务端计算时间的流程很短
      • 回调计算时间先执行

      若是这个同步是很长时间以前同步的呢,可能时间过了好久后已经相差较多了。缓存

      • 定时同步,半小时一次

      若是客户修改系统时间怎么办服务器

      • 记录客户端的周期时间序列(这个周期能够设置的短一些,好比10秒这样子),第一次为基准,先作客户端与服务端同步,获得差值 T
      • 在第二个周期,计算两个时刻的时间差,再减去周期时间,就是相差的时间,若是在某一个很小的范围内,说明没有问题,若是不在范围内,说明可能修改了时间,那么修改 T 为 T + 差值

web 层

这一层要考虑限流问题,以及防止恶意刷量的问题。首先限流要尽可能在上层去作,以最大程度减小后端系统的压力。其次,要避免用户找到url,不停的大量发送网络请求,或者在活动前就发送,这样也是有问题的。

  • 限流

    这属于分布式限流,通常采用 redis 来作限流,能够用令牌桶来作

  • 防止提早刷 url

    这个能够在服务端根据系统时间来决定要不要处理,也能够用一个随机的网址来保证没法模拟 url 请求(这个点还比较模糊)
    并且这里涉及到服务端各服务器的时钟同步

  • 同一个 url

    这里能够用 redis 记录或者本地记录来进行计数过滤,保证用户每秒发送请求响应次数不超过一个阈值

service 层

  • 若是有库存,而且拿到了资源,再生单的逻辑顺序
  • 解决并发问题的思路
      • 悲观锁

        性能比较差

      • 乐观错

        性能好些

    • 缓存

      缓存来保存库存量,减小访问 mysql 带来的并发,用 redis 能够作到

      可是若是在拿到资格后出现问题,怎么办?在缓存里已经被减掉了,这时须要归还资格,不然卖出的数就会少,这个错误可能会出如今生单,订单入库的阶段,直到入库,这个资格才能算做完全被消费掉

数据存储层

mysql 更合适,有惟一键的限制,hbase 存放海量数据

同步仍是异步的问题

  • 同步

    好处,等待结果写入库里,彻底闭环

  • 异步

    可能会写入失败,丢失订单信息,由于订单详情是要尽快展现给用户的,因此一旦失败,该取消此次秒杀的结果,仍是继续认为成功,是比较棘手的问题。异步出错了,可能能够修复,可是也可能会一直出错,重试无效。这种应该归还,而后把结果通知给用户。若是异步默认生单成功,可是怎么也写不进去,那就会有问题了。(这块每太想清楚)

    先说说异步作法

    • 交由本地线程池处理

      占用 service 层资源

    • 发送 kafka

      减小了 service 层资源占用,可是要保证 kafka 可靠,这里须要保证 有副本,ack -> ALL,replica 设置>1

其余问题

  • 如何保证同一个用户只能下一个单

    • 若是是用 redis 来作
      • 那么能够在获取权限的前一层写入,作第一层判断
      • 也就是若是获取到一次资格,立马锁定,若是后面的生单失败了,再解锁
    • 若是用 mysql 来作
      • 会致使在最下层才判断出来,而前面已经拿到了资格,致使不少人没抢到,这时应该返回什么?通常返回失败,直到全局的秒杀结束,再通知结束。
  • 由于资源被占用后,后续不必定生单成功,因此若是资源没了,不该该直接展现秒杀结束

    • 要有一个全局的标识,确认秒杀结束
  • server 端时间同步

    • 服务启动时以某一个为基准就好,能够是集群中的,也能够是集群外的
相关文章
相关标签/搜索