一个3人团,剩1个名额,100人同时抢…我把从崩溃到高可用的架构演进讲透了
2026/1/3大约 7 分钟面试题面试题

想象一个场景:一个热门商品的三人团,已经有两人加入,只剩下最后一个宝贵的席位。就在这时,系统检测到有100个用户在同一秒点击了“加入拼单”按钮。会发生什么?
在一个没有经过特殊设计的“脆弱”系统中,答案是灾难。
这100个请求会像洪水一样涌入,同时去数据库里查询“名额还剩1个”,然后争先恐后地去扣减名额。结果就是,系统瞬间卖出了远超1个名额的订单,造成并发超卖。紧接着,数据库因为瞬时的大量写入和锁竞争而崩溃,整个服务宕机。
这,就是我们今天要面对的核心挑战。表面上看,拼团的流程风平浪静:

但实际上,在“B/C同时加入”这一步,就是所有问题的风暴中心。如果没有任何保护,系统就会在这里被瞬间击穿。
核心挑战一:并发控制 - Redis锁化身“检票员”

要解决100人抢1个座位的问题,我们最先想到的,也是最容易犯错的方案,就是直接给数据库加锁。
- 错误示范:数据库悲观锁 这相当于一个简单粗暴的管理员,直接在数据库商品库存那一行挂上了一把大锁 (
SELECT ... FOR UPDATE)。第一个请求来了,锁住!在它完成所有操作(减库存、创订单)并释放锁之前,后面99个请求全部在门外排队干等着。在低并发下这没问题,但在高并发场景,这会造成大量请求堵塞,性能发生雪崩。 - 正确方案:Redis分布式锁,化身“检票员” 正确的做法,是请一位手速极快的“检票员”站在数据库的大门前,这位检票员就是Redis。我们利用Redis的原子命令(如
SETNX,即SET if Not eXists)来实现分布式锁。 100个请求同时到达,但只有一个请求能成功在Redis里创建那个代表“锁”的key。它成功抢到了“门票”。 其他99个请求,发现票已经被抢走了,就会被“检票员”礼貌地告知“座位已满”,然后直接返回失败或进入一个短暂的等待队列。 整个过程发生在内存中,快如闪电。只有拿到票的那个幸运儿,才能慢悠悠地去操作数据库。这样,既保证了“一人一单”,又把巨大的并发压力挡在了数据库之外,优雅而高效。
核心挑战二:超时处理 - 告别“人工巡逻”,拥抱“智能闹钟”

另一个经典问题是:一个拼单发起了,但24小时内没凑齐人,系统该如何发现并自动给已付款的用户退款?
- 低效方案:定时任务“人工巡逻” 最笨的办法,就是写个定时任务,像个保安一样,每分钟都去数据库里“巡逻”一遍,把所有状态为“拼单中”的订单都捞出来,逐一检查它们是否超时。订单量少的时候还行,一旦订单量上万、上百万,这种全表扫描式的“巡逻”会对数据库造成毁灭性的打击。
- 优雅方案:MQ延迟消息,设定“智能闹钟” 我们需要的不是保安,而是为每个订单配一个“专属闹钟”。这个“智能闹钟”就是消息队列(MQ)的延迟消息功能。 当一个拼单创建时,我们不只在数据库里创建订单,还同时向MQ发送一条延迟消息,比如,延迟24小时投递。 在接下来的24小时里,我们的系统可以完全“忘记”这件事,对数据库零压力。24小时后,MQ会自动将这条消息推送给我们的处理服务。服务收到消息后,去检查一下这个订单的状态,如果还是“拼单中”,就执行退款流程。精准、高效,对数据库的消耗降到了最低。
核心挑战三:可靠通知 - 巧用“发件箱底稿”模式

拼单超时,需要退款。我们的拼单服务需要通知支付服务去执行退款操作。但如果这次通知因为网络抖动、支付服务宕机而失败了,消息就丢了,用户的钱就退不了,这可是天大的事故。
- 问题所在:直接远程调用 直接通过RPC或HTTP调用,就是把所有希望寄托在“网络永远可靠”这个不切实际的幻想上。
- 可靠方案:本地消息表,巧用“发件箱底稿”模式 为了确保消息100%送达,我们可以借鉴写邮件的“草稿箱”思路。这个模式在技术上被称为“本地消息表”或“事务性发件箱”。
- 先记账,再通知:在执行“更新订单状态为已退款”的这个数据库事务里,我们同时在另一张“本地消息表”里插入一条记录,内容是“需要通知支付服务为订单XXX退款”。这两个操作在同一个事务里,保证了原子性。
- “检查员”定时核对:有一个独立的、可靠的后台任务,像个“检查员”,它会定时扫描这张“本地消息表”。
- 发送与确认:检查员发现有未发送的消息,就调用支付服务的接口。调用成功后,再把这条消息标记为“已发送”。如果调用失败,没关系,因为消息还在表里,下次检查员巡逻时会再次尝试,直到成功为止。 这样,就确保了退款通知“使命必达”。
最终架构:构建稳健的拼单服务
现在,我们将以上所有关键技术点整合起来,就得到了一个稳健、高可用的拼单服务最终架构:

- 入口:海量并发请求涌入。
- 核心处理层:
- Redis“检票员” 负责顶住第一波并发冲击,完成分布式锁。
- MQ“智能闹钟” 负责处理所有超时、延迟类任务。
- “发件箱底稿”(本地消息表) 机制与一个可靠消息服务配合,确保跨服务的最终一致性。
- 一个订单状态机 在内部管理订单从“拼单中”到“成功”或“失败”的流转。
- 底层:所有状态最终都将持久化到数据库中。
通过这套组合拳,我们构建的系统,才能在复杂多变的生产环境中,稳如泰山。