秒杀系统

月伴飞鱼 2024-09-09 18:35:33
系统设计
支付宝打赏 微信打赏

如果文章对你有帮助,欢迎点击上方按钮打赏作者!

秒杀要解决的主要问题是:并发读并发写

API设计原则

保证用户请求的数据尽量少、请求数尽量少、路径尽量短、依赖尽量少,不要有单点

秒杀架构原则

高可用

  • 整个系统架构需要满足高可用性,流量符合预期的时候肯定要稳定

一致性

  • 数据必须一致,即成交总量必须和设定的数量一致

高性能

  • 系统的性能要足够强,支撑足够大的流量

动静分离

将用户请求的数据划分为动态数据静态数据

对分离出来的静态数据做缓存

页面静态化

活动页面上绝大部分内容都是固定的,比如:商品描述、图片等

没有必要每次都去请求服务端,而是将这些静态的内容放到 CDN

  • 每次打开页面的时候,直接去请求 CDN 服务器,能极大地减少后端的请求流量
图片

MQ异步处理

秒杀活动一般涉及抢购、下单、支付、发货等阶段

  • 而抢购与后续的几个阶段是可以异步执行的

为了避免对下单、支付、发货等阶段产生影响,可以将抢购阶段与后续阶段用 MQ 进行解耦处理

  • 当用户抢购成功后,往消息队列中丢入MQ消息,随后再由订单系统消费进行下单处理

预热数据

在秒杀活动开始之前,可以手动将热点数据加载到缓存中,从而避免秒杀时去请求数据库。

限流、熔断、降级

限流

在每个业务系统做限流操作,从而避免因为请求太多,导致整个系统都无法工作。

  • 当并发请求在正常范围内时,正常处理请求。

  • 当超过设置的限流阈值时,则直接拒绝该请求,提示用户抢购失败。

熔断

请求的错误次数超过阈值时,不再到用后端服务,直接返回失败。

  • 同时每隔一定时间放几个请求去重试后端服务,看看是否正常。

  • 如果正常则关闭熔断状态,如果失败则继续快速失败。

熔断的目的是:

  • 避免因下游短暂的异常,导致上游不断重试,最终造成下游有太多请求,最终压垮下游系统。

降级

当服务失败或异常后,返回指定的默认信息。

  • 当下游异常时,与其返回空信息,不如返回一个有业务含义的默认信息,可以提高用户体验。

减库存

下单减库存

即当买家下单之后,在商品的总库存中减去买家购买的数量

这种方式控制最精确,下单时直接通过数据库的事务机制控制商品库存,这样一定不会出现超卖的现象

  • 但是有些人下完单以后并不会付款

付款减库存

即买家下单后,并不立即减库存,而是等到有用户付款后才真正减库存,否则库存一直保留给其他买家

但因为付款时才减库存,如果并发比较高,有可能出现买家下单后付不了款的情况

  • 因为可能商品已经被其他人买走了

预扣库存

买家下单后,库存为其保留一定的时间(如 10 分钟),超过这个时间,库存将会自动释放

  • 释放后其他买家就可以继续购买

在买家付款前,系统会校验该订单的库存是否还有保留:

  • 如果没有保留,则再次尝试预扣
  • 如果库存不足(也就是预扣失败)则不允许继续付款
  • 如果预扣成功,则完成付款并实际地减去库存

避免超卖

在通过事务来判断,即保证减后库存不能为负,否则就回滚

直接设置数据库字段类型为无符号整数,这样一旦库存为负就会在执行 SQL 时报错

使用CASE WHEN判断语句

UPDATE item SET inventory = CASE WHEN inventory >= xxx THEN inventory-xxx ELSE inventory END

高并发写

解决并发锁的问题,有两种办法

应用层排队

通过缓存加入集群分布式锁,从而控制集群对数据库同一行记录进行操作的并发度

  • 同时也能控制单个商品占用数据库连接的数量,防止热点商品占用过多的数据库连接

数据层排队

应用层排队是有损性能的,数据层排队是最为理想的

业界中,阿里的数据库团队开发了针对InnoDB 层上的补丁程序,可以基于DB层对单行记录做并发排队

热点隔离

业务隔离:

  • 秒杀作为一种营销活动,卖家需要单独报名,从技术上来说,系统可以提前对已知热点做缓存预热

系统隔离:

  • 系统隔离是运行时隔离,入口层就让请求落到不同的集群中

数据隔离:

  • 秒杀数据作为热点数据,可以启用单独的缓存集群

热点优化

缓存限流

支付宝打赏 微信打赏

如果文章对你有帮助,欢迎点击上方按钮打赏作者!