首页 > 数据库 > 数据库开发技术 > 消息队列:秒杀时如何处理每秒上万次的下单请求?
在社区系统初始阶段,大部分用户只是观看内容,少量用户才会产生写操作。虽然整体流量可能达到每秒 10000 次,但写请求仅占其中的百分之一,即每秒仅有大约 100 次写请求。考虑到写请求相对较少,对其进行性能优化的成本效益比可能较低。
但随着业务发展,你可能会遇到一些存在高并发写请求的场景,其中秒杀抢购就是最典型的场景。假设你的商城策划了一期秒杀活动,活动在第五天的 00:00 开始,仅限前 200 名,那么秒杀即将开始时,后台会显示用户正在疯狂地刷新 APP 或者浏览器来保证自己能够尽量早的看到商品。
这时,你面对的依旧是读请求过高,那么应对的措施有哪些呢?
针对用户查询的少量商品数据,可以采用缓存策略来减轻数据库负载。具体做法包括:
缓存热点数据: 将用户频繁查询的商品数据缓存到内存中,例如使用Redis等缓存服务。这样可以减少对数据库的直接查询,提高响应速度和系统性能。
静态化处理: 对于能够被静态化的数据,如商城里的图片和视频数据,可以通过CDN(内容分发网络)进行静态化处理,使其能够命中CDN节点缓存,减少Web服务器的查询量和带宽负担。
限流策略: 实施限流控制,对短时间内来自某一用户、IP或设备的重复请求进行丢弃处理,以降低服务器负载,避免过多请求达到数据库。
通过以上缓存策略和限流控制,可以尽量将读请求挡在数据库之外,缓解数据库的压力。
然而,即使在读请求稍微缓解之后,在秒杀活动开始时,可能会出现大量的写请求,导致数据库连接数激增,进而使系统面临崩溃的风险。为了应对如此高并发的写请求,可以考虑引入消息队列。具体措施包括:
异步处理: 将用户的写操作(如生成订单、扣减库存等)转化为异步任务,放入消息队列中,由后台异步处理。这样可以削峰填谷,避免瞬时高并发导致数据库崩溃。
消息队列: 使用消息队列来承载大量的写请求,确保数据安全可靠。常见的消息队列包括Kafka、RabbitMQ等。
削去秒杀场景下的峰值写流量
在秒杀场景下,高并发写请求只出现在短暂的时间内,而分库分表或扩充数据库的操作对于这种瞬时的高峰并不切实际。因此,我们的解决方案是将秒杀请求暂时存储在消息队列中。业务服务器在释放系统资源后再处理这些请求,这样能够更有效地利用系统资源,确保秒杀活动的顺利进行。
我们会在后台启动多个队列处理程序,用于消费消息队列中的消息,并执行校验库存、下单等逻辑。由于只有有限数量的队列处理线程在执行,因此后端数据库上的并发请求是有限的。请求可以在消息队列中短暂堆积,一旦库存被消耗完毕,堆积在消息队列中的请求就可以被丢弃处理。这样可以有效地控制后端数据库的并发请求量,保证系统的稳定性和性能。

消息队列在秒杀系统中的主要作用就是削峰填谷,即平滑处理短暂的流量高峰。尽管堆积会导致请求短暂延迟处理,但通过监控消息队列的堆积情况,并在堆积量超过一定阈值时增加队列处理机的数量,可以提升消息的处理能力。此外,秒杀用户对于短暂延迟知晓秒杀结果也有一定的容忍度,因此这种处理方式能够满足用户的需求,并保证系统的稳定性和性能。
需要注意的是,我提到的延迟是指短暂的延迟。如果长时间不及时公示秒杀结果,用户可能会对秒杀活动产生怀疑。因此,在使用消息队列来处理流量峰值时,需要充分评估队列处理时间、前端写入流量的大小以及数据库处理能力等因素,并根据不同的情况来决定部署多少台队列处理程序,以确保及时公示秒杀结果,维护用户体验和信任度。
举例来说,如果你的秒杀商品有1000件,处理一次购买请求的时间是500毫秒,那么总共就需要500秒的时间来处理完所有购买请求。如果你部署了10个队列处理程序,那么每个请求的处理时间就会缩短为50秒。这意味着用户需要等待50秒才能看到秒杀的结果,这在大多数情况下是可以接受的。在这种情况下,并发处理10个请求不会给数据库造成过大的压力。
通过异步处理简化秒杀请求中的业务流程
在处理大量的写请求时,消息队列除了削峰填谷的作用外,还可以实现异步处理,从而简化秒杀请求中的业务流程,提升系统性能。在秒杀场景下,处理购买请求需要500毫秒。在分析整个购买流程后,我们发现其中包含主要业务逻辑和次要业务逻辑:
主要业务逻辑:生成订单、扣减库存。
次要业务逻辑:例如,下单购买成功后发放优惠券、增加用户积分等。
通过消息队列实现异步处理,我们可以将次要业务逻辑从主要业务逻辑中分离出来。具体做法是,将主要业务逻辑(生成订单、扣减库存)放入主要处理队列中,而将次要业务逻辑(发放优惠券、增加用户积分)放入次要处理队列中。这样可以保证主要业务逻辑的快速响应,而次要业务逻辑则在后台异步处理,不影响主流程的执行速度。通过这种方式,我们可以提升系统的性能和并发处理能力,更好地应对大量写请求的挑战。
假如发放优惠券的耗时是 50ms,增加用户积分的耗时也是 50ms,那么如果我们将发放优惠券、增加积分的操作放在另外一个队列处理机中执行,那么整个流程就缩短到了 400ms,性能提升了 20%,处理这 1000 件商品的时间就变成了 400s。如果我们还是希望能在 50s 之内看到秒杀结果的话,只需要部署 8 个队列程序就好了。
经过将一些业务流程异步处理之后,我们的秒杀系统部署结构也会有所改变:

解耦实现秒杀系统模块之间松耦合
除了用来异步处理和平滑流量峰值之外,在秒杀系统中,消息队列还扮演着解耦合的角色。
例如,假设数据团队要在秒杀活动结束后分析活动数据,以了解商品受欢迎程度、购买者特征以及用户满意度等指标。我们需要将大量数据发送给数据团队,该如何处理呢?

异步处理、解耦合和削峰填谷是消息队列在秒杀系统设计中的主要作用。异步处理能够简化业务流程,提升系统性能;削峰填谷可以平滑处理达到秒杀系统的高峰流量,使业务逻辑处理更加平稳;解耦合则能够将秒杀系统与数据系统解耦,使得两个系统之间的变更互不影响。
总结:
削峰填谷是消息队列最主要的作用,但是会造成请求处理的延迟。
异步处理是提升系统性能的神器,但是你需要分清同步流程和异步流程的边界,同时消息存在着丢失的风险,我们需要考虑如何确保消息一定到达。
解耦合可以提升你的整体系统的鲁棒性。
原文作者:二进制跳动;如有侵权,请联系管理员删除。