场景题:秒杀系统如何设计

23 阅读3分钟

最近在学习场景题,记录一下自己的学习。秒杀很常见,手机秒杀,茅台酒类的秒杀,为的就是以很低的价格购入超值的产品。

业务特点

  • 瞬间并发量大,定点秒杀的话,时间到来之前,已经开始接受考验了。
  • 库存少,上万人抢1个也是常见的
  • 业务流程比较简单

技术特点

  • 高并发,快响应
  • 防止超卖,固定少量库存
  • 防机器人等各类刷子
  • 页面资源访问较多
  • 秒杀按钮点击一次和秒杀链接的隐藏
  • 订单异步处理及订单处理失败补偿
  • 服务降级

架构设计

拙劣的画图

截屏2025-04-14 20.20.41.png

架构详解

看了好几个视频,这个比较让我能接受,自上而下的介绍相关的架构的设计

应用层

  • 需要将静态页面缓存到离的最近的CDN处,减少对于服务器的请求;
    • CDN(Content Delivery Network)主要存储静态网络资源,比如图片、视频、css。可以减少服务器的请求量,因节点较多可以提升高可用性。通过不断的CDN query,可以匹配最近的CDN的cname,然后确定具体的ip地址。
    • CDN相比于cache:节点数量多;距离远;数据类型较少;无超时设置
  • 秒杀按钮设计:秒杀开启前,按钮置灰,秒杀开始后,按钮点击一次后置灰,增加滑动验证码,避免机器人秒杀,增加用户排队体验。

负载层

  • 根据秒杀访问的QPS设计了四层负载均衡,第一层为F5/LVS,硬件级别的负载均衡器,第二层为Nginx的集群,可以实现限流,第三层为服务网关,可以进行限流和熔断,第四层为服务集群通过ribbon实现负载均衡。
  • 利用云环境的动态扩容机制,秒杀开始前增加云机器数量,结束时回收云机器

中间件层

  • 使用redis储存热点信息,进行库存的增减,
    • 使用redis的lua脚本,比对库存是否大于零,将获取到的数据减一,替换原来的库存,表示秒杀成功一次
    • setNX加锁,防止重复秒杀
    • 增加分布式锁
  • MQ
    • 支持异步下单
    • 限流
    • 订单失败通知补偿策略

数据层

使用mysql的读写分离机制

细节

在高并发的订单处理下,如何避免超卖呢?

1 在数据库设置乐观锁(version),通过新旧版本的比对实现更新库存,减少超卖情况

start tranction;
select stock,version from orders where id=xxx;
if stock >= order_require:
  update orders set stock = stock -order_require,version=version+1
  where id=xxx and version=old_version;
  if row >0:
      insert into pruducts(order_id,quantity) values(1,order_require);
      commit;
  else:
    rollback;
  end if;
else
  rollback;
end if;

2 设置redis的分布式锁,在进行同一商品的一个用户的订单的处理的时候,确保仅一个服务进行,保证服务幂等性