消息队列:秒杀时如何处理每秒上万次的下单请求?

本文探讨了如何利用消息队列解决秒杀场景下的高并发问题,通过削峰填谷、异步处理和解耦合的方式提高系统性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们了解了高并发系统设计的三个目标:性能、可用性和可扩展性,而在提升系统性能方面,我们一直关注的是系统的查询性能。也学习了数据库的分布式改造,各类缓存的原理和使用技巧。究其原因在于,我们遇到的大部分场景都是读多写少,尤其是在一个系统的初级阶段。

比如说,一个社区的系统初期一定是只有少量的种子用户在生产内容,而大部分的用户都 在“围观”别人在说什么。此时,整体的流量比较小,而写流量可能只占整体流量的百分之 一,那么即使整体的 QPS 到了 10000 次 / 秒,写请求也只是到了每秒 100 次,如果要对写请求做性能优化,它的性价比确实不太高。

但是,随着业务的发展,我们可能会遇到一些存在高并发写请求的场景,其中秒杀抢购就是最典型的场景。假设我们的商城策划了一期秒杀活动,活动在第五天的 00:00 开始,仅限前 200 名,那么秒杀即将开始时,后台会显示用户正在疯狂地刷新 APP 或者浏览器来保证自己能够尽量早的看到商品。

这时,我们面对的依旧是读请求过高,那么应对的措施有哪些呢

因为用户查询的是少量的商品数据,属于查询的热点数据,我们可以采用缓存策略,将请求尽量挡在上层的缓存中,能被静态化的数据,比如说商城里的图片和视频数据,尽量做到静态化,这样就可以命中 CDN 节点缓存,减少 Web 服务器的查询量和带宽负担。Web 服务器比如 Nginx 可以直接访问分布式缓存节点,这样可以避免请求到达 Apache等业务服务器。

当然,也可以加上一些限流的策略,比如,对于短时间之内来自某一个用户、某一个 IP 或 者某一台设备的重复请求做丢弃处理。

通过这几种方式,我们发现自己可以将请求尽量挡在数据库之外了。

稍微缓解了读请求之后,00:00 分秒杀活动准时开始,用户瞬间向电商系统请求生成订单, 扣减库存,用户的这些写操作都是不经过缓存直达数据库的。1 秒钟之内,有 1 万个数据库连接同时达到,系统的数据库濒临崩溃,寻找能够应对如此高并发的写请求方案迫在眉睫。这时我们想到了消息队列。

我所理解的消息队列

关于消息队列是什么,你可能有所了解了,所以有关它的概念讲解,就不是我们学习的重点, 这里只聊聊我自己对消息队列的看法。在我历年的工作经历中,我把消息队列看作是一个暂时存储数据的一个容器,我认为消息队列是一个平衡低速系统和高速系统处理任务时间差的工具,我们举个形象的例子。

比如说,在公司,我们通常需要请示boos,陈述一些业务上的事情,但是如果我们一拥而上,你一句我一句,那么老板肯定头都大了,也无法理解每个人要表达什么意思,所以呢,一般我们会一个一个的去boos办公室诉说自己的看法,老板听完一个人的,再听下一个,这就是消息队列。我们可以把boos的办公室看作是消息队列。

其实,你在一些组件中都会看到消息队列的影子:

  • 在 go的协程与channel,我们就会使用一个队列来暂时存储提交的任务,等待协程空闲再来处理这些任务;
  • 操作系统中,中断的下半部分也会使用工作队列来实现延后执行;
  • 我们在实现一个 RPC 框架时,也会将从网络上接收到的请求写到队列里,再启动若干个工作线程来处理。

总之,队列是在系统设计时一种常见的组件。

那么我们如何用消息队列解决秒杀场景下的问题呢?接下来,我们来结合具体的例子来看看消息队列在秒杀场景下起到的作用。

削去秒杀场景下的峰值写流量

刚才提到,在秒杀场景下,短时间之内数据库的写流量会很高,那么依照我们以前的思路应该对数据做分库分表。如果已经做了分库分表,那么就需要扩展更多的数据库来应对更高的写流量。但是无论是分库分表,还是扩充更多的数据库,都会比较复杂,原因是我们需要将数据库中的数据做迁移,这个时间就要按天甚至按周来计算了。

而在秒杀场景下,高并发的写请求并不是持续的,也不是经常发生的,而只有在秒杀活动开始后的几秒或者十几秒时间内才会存在。为了应对这十几秒的瞬间写高峰,就要花费几天甚至几周的时间来扩容数据库,再在秒杀之后花费几天的时间来做缩容,这无疑是得不偿失的。

所以,我们的思路是:将秒杀请求暂存在消息队列中,然后业务服务器会响应用户“秒杀结 果正在计算中”,释放了系统资源之后再处理其它用户的请求。

我们会在后台启动若干个队列处理程序,消费消息队列中的消息,再执行校验库存、下单等逻辑。因为只有有限个队列处理线程在执行,所以落入后端数据库上的并发请求是有限的。而请求是可以在消息队列中被短暂地堆积,当库存被消耗完之后,消息队列中堆积的请求就可以被丢弃了。
在这里插入图片描述
这就是消息队列在秒杀系统中最主要的作用:削峰填谷,也就是说它可以削平短暂的流量高峰,虽说堆积会造成请求被短暂延迟处理,但是只要我们时刻监控消息队列中的堆积长度, 在堆积量超过一定量时,增加队列处理机数量,来提升消息的处理能力就好了,而且秒杀的用户对于短暂延迟知晓秒杀的结果,也是有一定容忍度的。

这里需要注意一下,上面所说的是“短暂”延迟,如果长时间没有给用户公示秒杀结果,那么用户可能就会怀疑我们的秒杀活动有猫腻了。所以,在使用消息队列应对流量峰值时,需要对队列处理的时间、前端写入流量的大小,数据库处理能力做好评估,然后根据不同的量级来 决定部署多少台队列处理程序。

比如我们的秒杀商品有 1000 件,处理一次购买请求的时间是 500ms,那么总共就需要 500s 的时间。这时,部署 10 个队列处理程序,那么秒杀请求的处理时间就是 50s,也就是说用户需要等待 50s 才可以看到秒杀的结果,这是可以接受的。这时会并发 10 个请求到达数据库,并不会对数据库造成很大的压力。

通过异步处理简化秒杀请求中的业务流程

其实,在大量的写请求“攻击”我们的系统的时候,消息队列除了发挥主要的削峰填谷的作用之外,还可以实现异步处理来简化秒杀请求中的业务流程,提升系统的性能。

想一下,在刚才提到的秒杀场景下,我们在处理购买请求时,需要 500ms。这时,分析了一下整个的购买流程,发现这里面会有主要的业务逻辑,也会有次要的业务逻辑:比如说, 主要的流程是生成订单、扣减库存;次要的流程可能是我们在下单购买成功之后会给用户发放优惠券,会增加用户的积分。

假如发放优惠券的耗时是 50ms,增加用户积分的耗时也是 50ms,那么如果我们将发放优惠券、增加积分的操作放在另外一个队列处理机中执行,那么整个流程就缩短到了400ms,性能提升了 20%,处理这 1000 件商品的时间就变成了 400s。如果我们还是希望 能在 50s 之内看到秒杀结果的话,只需要部署 8 个队列程序就好了。

经过将一些业务流程异步处理之后,我们的秒杀系统部署结构也会有所改变:
在这里插入图片描述

解耦实现秒杀系统模块之间松耦合

除了异步处理和削峰填谷以外,消息队列在秒杀系统中起到的另一个作用是解耦合。

比如数据分析团队对我们说,在秒杀活动之后想要统计活动的数据,借此来分析活动商品的受欢迎程度、购买者人群的特点以及用户对于秒杀互动的满意程度等等指标。而我们需要将大量的数据发送给数据分析团队,那么要怎么做呢?

一个思路是:可以使用 HTTP 或者 RPC 的方式来同步地调用,也就是数据团队这边提供一 个接口,我们实时将秒杀的数据推送给它,但是这样调用会有两个问题:

  • 整体系统的耦合性比较强,当数据分析团队的接口发生故障时,会影响到秒杀系统的可用性。
  • 当数据系统需要新的字段,就要变更接口的参数,那么秒杀系统也要随着一起变更。

这时,我们可以考虑使用消息队列降低业务系统和数据系统的直接耦合度。

秒杀系统产生一条购买数据后,我们可以先把全部数据发送给消息队列,然后数据分析团队再订阅这个消息队列的话题,这样它们就可以接收到数据,然后再做过滤和处理了。

秒杀系统在这样解耦合之后,数据系统的故障就不会影响到秒杀系统了,同时,当数据系统需要新的字段时,只需要解析消息队列中的消息,拿到需要的数据就好了。

在这里插入图片描述
异步处理、解耦合和削峰填谷是消息队列在秒杀系统设计中起到的主要作用,其中,异步处理可以简化业务流程中的步骤,提升系统性能;削峰填谷可以削去到达秒杀系统的峰值流量,让业务逻辑的处理更加缓和;解耦合可以将秒杀系统和数据系统解耦开,这样两个系统的任何变更都不会影响到另一个系统。

如果想要系统想要提升写入性能,实现系统的低耦合,想要抵挡高并发的写流量,那么就可以考虑使用消息队列来完成。

总结

我们了解了,消息队列在高并发系统设计中起到的作用,以及一些注意事项,我们需要了解的重点如下:

  • 削峰填谷是消息队列最主要的作用,但是会造成请求处理的延迟。
  • 异步处理是提升系统性能的神器,但是需要分清同步流程和异步流程的边界,同时消息存在着丢失的风险,我们需要考虑如何确保消息一定到达。
  • 解耦合可以提升整体系统的鲁棒性。

当然,我们要知道,在使用消息队列之后虽然可以解决现有的问题,但是系统的复杂度也会上 升。比如上面提到的业务流程中,同步流程和异步流程的边界在哪里?消息是否会丢失,是否会重复?请求的延迟如何能够减少?消息接收的顺序是否会影响到业务流程的正常执行?如果消息处理流程失败了之后是否需要补发?这些问题都是我们需要考虑的。接下来我们就要学习,针对最主要的两个问题来讲讲解决思路:一个是如何处理消息的丢失和重复, 另一个是如何减少消息的延迟

欢迎扫码关注V信公众号一起交流:一苦四甜

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值