🧑💻 面试官:一个商品有 100 件库存,活动开始后大量请求一起进来,怎么设计?
🙋♂️ 我:先限流,用 Redis 原子扣库存,再通过队列创建订单。
🧑💻 面试官:库存扣了,消息没发出去呢?订单超时,库存返还了,支付成功的回调又来了呢?
🙋♂️ 我:还需要预占记录、订单状态和补偿,不能只做一次减法。
🧑💻 面试官:Redis 故障以后,数据库还有库存。可以直接让所有请求打数据库继续买吗?
这道题不是只考「扣减是否原子」,还要讲清「准入、预占、成交」之间的关系:请求进来不等于买到,预占成功也不等于付款完成。
面试速答(60 秒版)
秒杀首先需要保护系统容量。静态资源尽量提前分发,入口做身份校验、重复请求限制与限流,不让所有请求直接进入库存和数据库。
库存检查与预占必须在定义好的范围内原子完成,同时记录请求或预占 ID,避免重复请求反复扣减。Redis 可以承担高频准入和预占,但数据库仍要有不超卖的约束与最终状态管理。
预占以后,再可靠推进订单创建、支付和超时释放。队列可以削峰,却不能消除消息重复、投递失败及状态竞争,需要幂等、补偿和对账。
最后要明确故障策略:缓存或队列故障时不能无界打到数据库;对于库存一致性与可用性的取舍,应由同一套权威库存和状态规则决定,而不是临时绕过。

知识点详解:一次请求怎样从排队变成真正买到?
先让能处理的请求进来
活动页面、商品介绍和图片,可以提前准备并通过缓存或 CDN 分发。它们没有必要都挤到交易链路。
真正购买请求则需要按系统容量准入,并检查用户、活动时间和重复提交。限流能减少压力,但不是防机器人和公平性的完整方案,还可能需要针对账户、设备及异常行为的规则。
队列也不是无限容量的保险箱。消费者速度固定,生产持续更快,积压就会增长。需要队列上限、等待时长、过载拒绝和可查询的状态。
原子预占,要把“判断”和“修改”放在一起
如果先读库存大于零,再分开扣减,多个请求可能同时读到最后一件,随后都成功进入订单流程。
可以在库存服务的事务或原子操作中一起完成校验、预占和必要的请求去重。Redis 脚本可以使对应操作在其执行范围内原子完成,但要注意 Cluster 的键分布及原子范围。
Redis INCR 文档展示了计数及限流中的原子操作,也提醒组合命令可能存在竞态。单条命令原子,不等于整条业务链路原子。
因此,分布式锁不是唯一答案。首先明确竞争资源、库存权威和操作边界,再选工具;锁本身也不能替代订单与消息的一致性。
预占成功以后,还欠用户一笔订单
预占只表示暂时留了一件,并不表示订单已经成功,更不表示付款完成。
一种教学方案,是保存可追踪的预占记录,由可靠投递过程推进订单创建。消费者按预占 ID 幂等创建,完成以后把状态关联回来。
如果消息发送结果不确定,不能直接再扣一件;应该查询已有预占,重试推进同一笔记录。如果预占已经失败或释放,也不能把迟到的消息当成新购买成功。
缓存里的高速计数还需要与权威记录对账。数据库可用条件更新等方式限制最终库存不低于允许范围;它应是完整方案里的防线,而不是缓存故障时无界承接全部请求。
超时释放与支付成功,为什么会打架?
订单等待支付时预占库存;到期后准备取消;此时支付回调可能同时到达。
不能一边无条件返还库存,另一边无条件改成已支付。需要通过订单状态的条件迁移,判断谁已取得有效状态,并只执行对应的库存动作。
| 当前状态 | 合法推进示例 | 需要限制的动作 |
|---|---|---|
| 已预占 | 创建待支付订单 | 重复扣减库存 |
| 待支付 | 成功支付或超时取消 | 同时认定两种结果 |
| 已取消 | 按规则处理迟到支付 | 直接复活已释放的库存 |
| 已支付 | 履约或退款流程 | 再次按超时返还 |
具体规则可能是拒绝、退款,或重新确认库存后另行处理。关键在于规则事先明确,状态迁移与外部支付结果可核对。
补偿还需要幂等,不能一次超时任务重复跑,就返还多件库存。

故障时,宁可排队或拒绝,也不要绕过保护
Redis 不可用以后,如果所有流量直接查库,缓存故障就可能升级成数据库故障。
可以选择关闭活动、收紧准入、受控切换或只接受明确容量内的请求。若切换库存权威,还要先确认旧预占怎样迁移、旧请求怎样停止,避免两个来源各自放行。
压测时不仅看每秒请求数,还要模拟重复请求、发送确认丢失、队列积压、消费者崩溃和迟到支付。最后检查库存、预占、订单与支付是否能对上。

面试官继续追问
Redis 原子扣减,就能保证永不超卖吗?
不能单独作这个承诺。原子范围内没有竞态,不代表故障切换、库存初始化、重复补偿和数据库落单都正确。需要说明权威与完整故障模型。
请求进入队列,就可以提示购买成功吗?
不应该。通常只能提示受理或排队,再查询实际结果。提前说成功,会把未完成的业务结果承诺给用户。
每个请求都加锁,是不是最稳?
不一定。可能把吞吐压低,也增加租约和故障处理问题。应根据原子操作、唯一约束和状态迁移设计竞争控制,不把锁当成系统设计的总答案。
面试速记卡
- 准入:先保护容量,队列也要有积压与等待边界。
- 预占:校验、去重与修改在明确范围内原子完成。
- 成交:预占、订单和支付分别记录,不提前宣告成功。
- 释放:条件迁移与幂等补偿,处理迟到支付。
- 故障:不无界打库,压测后核对库存与最终状态。
