本文目录
防超卖靠权威约束,缓存预扣与队列只能是经过验收的协调层
先明确库存的可售、预占、已售与释放状态,再在权威存储中原子判断与扣减,并为订单创建和重复提交设计幂等约束。热点流量可通过入口筛选、有限排队与预热减少无效竞争,但“排队成功”不能表示订单已创建。Redis 脚本能在其执行范围内原子处理库存判断,却不会自动完成数据库订单或外部支付事务。只有把预扣、落库、支付、取消、释放和故障对账的状态闭环验证,才有可交付的库存一致性。
库存方案按一致性与故障闭环判断
| 方案或环节 | 可成立的条件 | 主要风险 |
|---|---|---|
| 数据库条件扣减 | 检查余量与扣减在同一原子语句或受控事务中,订单约束对应 | 先读余量再无条件更新,出现丢失更新或负库存 |
| 有限排队 | 业务允许等待,队列有上限且受理状态可查询 | 受理被宣传为成交,故障后用户无法确认结果 |
| 缓存预扣 | 有稳定预约标识、最终落库与失败补偿、恢复对账 | 缓存成功但订单未创建,或重试导致重复扣减 |
| 过期释放 | 支付与释放竞争通过状态条件和版本控制 | 订单已付款,释放任务仍把库存加回 |
适用边界:普通交易不要直接套复杂秒杀架构
本文面向少数商品或名额形成集中写竞争的活动。若压力主要来自图片、列表和配置读取,应先优化分发与缓存;普通订单流程不应为了“高并发”无条件增加异步预扣和补偿复杂度。
先定义库存单位、仓库与活动范围,确认一件商品是否可同时参加多活动,退货和取消如何影响可售数量。实物、电票、预约名额与多规格套餐的约束不同。跨仓组合与外部供应链还涉及多方事实,单个缓存计数器不能代表所有库存。
业务不变量应写成可以检查的关系,包括同一预约不能既释放又成交、同一购买意图不会创建多个可履约订单,以及总扣减与最终订单数量一致。不同取消和退款类型能否补回库存,需要业务明确规则;售后退款不能自动等同于活动可售数量增加。
- 确定谁是库存权威来源以及每次扣减对应的业务对象。
- 写清售罄、排队、预约、付款与订单成立的用户含义。
把检查余量和扣减放进同一受约束操作
先读取库存大于零,再执行无条件减一,会在并发时产生竞争。应由权威存储原子地判断数量和扣减,并检查受影响行数或明确失败结果;涉及订单创建时同步考虑事务边界。
以 PostgreSQL 的简单单行条件更新为例,在 Read Committed 隔离级别下会对并发更新后的行重新判断条件;Repeatable Read 下可能发生序列化失败,复杂跨行规则也需要额外约束与验证。多个商品按固定顺序锁定可降低部分死锁风险,仍要处理事务失败。不要把某个引擎默认隔离级别等同于所有业务约束自动成立。
- 数据库为唯一订单标识、业务幂等键与必要库存边界建立约束。
- 扣减失败应返回明确业务结果,不能静默继续生成可付款订单。
重复提交与结果未知都要有稳定业务标识
用户连点、网关重试和网络断开都可能重复提交。同一购买意图应能识别为同一受理或订单,客户端重试时查询既有结果,而不是再创建一份订单、再扣一次库存。
幂等键需要绑定账号、活动和请求内容,避免同一个键对应不同购买条件。数据库提交后响应丢失是“结果未知”,不等于业务失败;使用稳定请求标识重新查询结果比直接补偿安全。恶意大量不同标识的请求还需要账号与活动配额,幂等不是流量治理的替代。
- 约定幂等键生成方、有效期和内容不一致时的错误语义。
- 验证重复请求得到相同业务标识且库存只受一次影响。
缓存预扣和队列需要明确跨组件断点
在 Redis 内使用短脚本可合并余量判断与预扣,脚本原子执行期间会阻塞其他服务活动,因此应限制复杂度。这个原子范围不包含数据库和支付;预扣成功后仍可能在下一步失败。
对每个断点设计可查询状态:预扣后未发送、发送后未落库、落库后响应丢失、支付后未更新。若采用预约记录或事务内待发布记录,应有恢复扫描与幂等处理。库存分桶会引入额度分配与回收问题,不能只证明局部计数器不为负,就声称全局库存正确。
- 受理结果只展示真实状态,并提供状态查询或最终通知。
- 有限排队之外的请求及时拒绝,不让等待与重试无限增长。
实施步骤:先验证状态机,再做热点优化
先画库存与订单状态转换和不变量,再实现原子约束与幂等,接着验证支付、取消与释放竞争,最后添加入口筛选、预热、排队或预扣,并在相同热点负载下对比收益。
过期释放要根据订单当前状态执行条件转换:已支付的预约不能再释放,已释放的预约不能重复加库存。支付在释放后到达时,业务必须有重新占用、退款或人工处理规则。恢复任务也要受限流,不能在活动结束后把大量补偿集中打到数据库。
- 为每个状态转换标明执行方、允许前态和重复执行结果。
- 保存补偿原因、关联订单与对账记录,不能只修改计数器后丢掉证据。
验收:验证不变量,比展示抢购 QPS 更重要
验收同时模拟同一商品高竞争、重复提交、响应丢失、进程退出、支付与释放交错。通过库存与订单对账确认可售、预占、已售和已释放关系成立,再看约定流量下的延迟与完成量。
故障期间允许受控拒绝,不应以超卖或丢订单换取漂亮成功率。恢复后每个预约都能查到归属与结果,无法自动判断的记录进入明确人工流程。性能结论必须说明活动库存、用户限制、数据规模和热点分布;均匀访问不同商品不能证明单个热商品容量。
对账应包含活动前初始库存、活动期间新增或调整、预约与成交、取消释放和人工变更。每次调整记录关联原因与操作人,避免测试发现差额后直接修计数器。若库存恢复存在不确定记录,应暂停相关预约继续流转,先查询最终事实再恢复履约。
- 验证重复支付通知、取消与释放不会重复扣减或回补。
- 保留热点压力曲线、事务失败与最终库存对账结果。
需求讨论中的问题
Redis Lua 扣库存就能保证绝不超卖吗?
它能在 Redis 执行范围内原子地完成判断和修改,不能自动把数据库订单、支付和恢复动作合成一个事务。还需权威库存规则、稳定标识、失败补偿与对账。
进入队列可以告诉用户购买成功吗?
如果只是受理或排队,应如实显示等待状态。订单已成立、已预占和已付款是不同结果,需要明确状态查询和最终确认,避免受理失败后用户无从判断。
支付和库存过期释放同时发生怎么办?
用受约束的状态转换保证只有合法前态能付款或释放,并规定支付晚到时的处理。测试两种先后顺序、重复执行和进程退出,不能靠定时任务执行顺序推断正确。
热点库存与订单验收清单
可逐项勾选,辅助本次阅读;刷新页面后不保留。
参考资料
技术概念可进一步阅读以下资料;文中的业务场景与实施清单用于方案讨论。
- PostgreSQL:Transaction Isolation
核验并发条件更新、锁等待与隔离级别的行为;跨行库存规则仍需具体设计。
- Redis:Scripting with Lua
核验脚本原子执行及阻塞语义,明确该范围不包含外部数据库或支付事务。
需要评估现有系统的性能瓶颈?
提供关键业务路径、真实峰值、服务器与数据库配置,以及现有压测或故障记录,月弦科技可据此梳理优化顺序、实施范围和可验证的验收条件。