系统设计

【系统设计】高性能秒杀系统如何设计?_消息队列

首先说一下 案例背景 :设计一个秒杀系统,秒杀系统的特性就是一瞬间峰值流量很大,远远大于常规时期的流量

如果对于这种峰值流量不采取应对措施的话,那么突然增大的流量就会导致系统负载升高,甚至系统瘫痪,所有业务都崩溃无法使用

加机器可以应对吗?

那我们先来思考一下通过添加机器是否可以应对下秒杀流量

那么对于这种尖刺流量,我们要做的第一步就是在活动开始之前,对于请求量进行预估,来给对应的业务增加机器,但是增加机器的话存在两个问题:

【系统设计】高性能秒杀系统如何设计?_Redis_02

第一个问题:增加机器数量不确定

如果机器节点增加少了,仍然会导致系统资源不够用的情况;如果机器节点增加多了,比较浪费资源

并且秒杀活动一般就持续几分钟,为了这几分钟来增加大量节点,要做许多运维工作以及评估流量工作,相对来说效率比较低

第二个问题:个别业务性能与机器数量无关

比如说 扣减库存 的业务,需要加上分布式锁去扣减库存,那么无论是加多少个机器节点,并发度都不会有提升,那么我们只有去对业务进一步优化,以此来提升系统的性能

秒杀应对方案

那么上边说了,通过添加机器会带来许多运维上的复杂度,并且不好评估,那么有没有其他方案来应对秒杀中的尖刺流量呢?

在高并发系统中,应对高并发流量通用的有 3 种方式:分流缓存异步

这里就是通过 异步 这种方式来应对秒杀中的峰值流量

在秒杀中,用户一瞬间会发送大量下单请求,如果每一个请求都直接去进行对应的业务处理(扣减库存、生成订单),那么系统在短时间内肯定是处理不完这么多的下单请求的

如果对每一个请求都去进行对应的业务处理,系统处于高负载状态,有些请求就会失败,返回超时提示,于是用户就重新刷新界面,发送更多的请求,对后台系统造成更大的压力,导致系统瘫痪

因此考虑使用对下单请求处理 异步化 ,当收到下单请求后,不立即进行处理,而是先放入到队列中,下单系统 根据自己的处理能力 来队列中取出下单请求进行处理

这样就可以避免整个系统因为处理大量的请求而处于高负载的状态了,整个流程如下:

【系统设计】高性能秒杀系统如何设计?_消息队列_03

  • 因此经过队列优化后,整个秒杀的下单流程为:

1、用户提交订单请求,请求进入到队列中,并返回用户一个排队编号

2、用户提交订单后,进入到等待界面中,显示用户前边还有多少请求等待处理,预计多长时间处理完毕

3、当用户的订单被下单系统成功处理之后,将用户界面跳转到支付页面,提示用户进行支付,之后就完成这笔订单

这就是使用了队列来进行削峰 ,将瞬时的峰值流量给分散铺平,避免系统的不稳定

技术选型

上边说到了使用队列对秒杀系统进行优化,那么队列该如何选型呢?

我们常用的消息队列有 RocketMQ、Kafka 等等,这里并没有选用这些消息队列,而是 使用了 Redis 来实现队列

相比于 RocketMQ 这些,Redis 更加轻量,并且基于内存操作,性能更好,使用 Redis 自带的队列数据结构,可以获取 队列长度 以及 请求在队列中的位置 ,可以及时反馈给用户排队进度

  • 接下来说一些技术细节

队列分配: 在 Redis 中给每一个秒杀商品都分配一个队列,这样将大量的请求再分散在多个队列中,队列的进入、退出操作不会成为秒杀的瓶颈,对该商品的下单请求就会进入到对应商品的队列中

队列调度: 下单系统根据下单时间和队列长度来 选择队列 进行处理,避免某些用户等待较长时间都得不到处理

队列长度: 队列长度设置为秒杀商品的 库存数量 ,当处理完一个请求后,就 将队列的长度 -1 ,当队列长度为 0 时,说明该商品就已经被抢完了,不要让后边的用户再进入到队列中进行等待了

秒杀数据库和其他库分离:

对于秒杀的商品来说,库存扣减的瞬时压力是比较高的,因此可以将秒杀的库单独抽取出来,和常规商品分离,这样也大幅度提升了库存数据库的读写能力

【系统设计】高性能秒杀系统如何设计?_Redis_04

秒杀系统的可用性:

在秒杀活动中,如果这个秒杀系统出现问题,我们要有 兜底方案

可以设计常规的商品处理流程为兜底方案,对于用户的请求,可以走 常规商品处理流程 ,也可以走 秒杀处理流程

这样在秒杀流程出现问题时,可以及时切换回常规商品的处理流程,一定程度上提升系统的可用性(主要是前期秒杀系统上线时,没有经过线上环境的历练,可能会存在一些问题)

【系统设计】高性能秒杀系统如何设计?_高并发_05