沐光而行 发表于 4 天前

秒杀百万并发不超卖!拆解电商三层库存防护体系,看懂抢到订单又被取消的底层真相

导语

双十一、618 等大促零点,几十万用户同时抢购库存仅百件爆款,所有人都在毫秒间提交下单请求。平台必须做到一件事:严格控制出库总量,绝不出现 “超卖”。一旦超卖,商家面临补发、赔付、平台口碑崩盘,是电商系统不能触碰的红线。

很多用户疑惑:明明页面显示有库存,点进去却提示售罄;有时页面提示抢购成功,没过几秒订单又自动取消。本文从超卖产生的底层并发冲突入手,完整拆解行业通用三层防超卖技术防线,同时盘点订单莫名取消的 6 类真实后台原因,用通俗语言讲清高并发秒杀背后的系统取舍逻辑。

一、根源:高并发下,为什么会出现商品超卖?


超卖的核心矛盾是先查询库存、再扣减库存两步操作存在时间间隙,专业称为竞态条件(并发冲突)。
完整错误执行流程:


[*]商品剩余库存 = 1;
[*]用户 A 请求到达,系统查询库存得到结果 1,准备执行扣减;
[*]毫秒间隙内,用户 B 同步发起请求,同样查询到库存为 1;
[*]A、B 先后执行库存 - 1,最终库存变为 0,生成两笔订单,但实际仅 1 件货,形成超卖。

普通单机低流量场景很难暴露该问题,只有秒杀瞬时几十万 QPS 冲击时,冲突概率被无限放大。单纯加锁、优化页面都无法根治,行业通用方案是网关限流 + Redis 缓存原子扣减 + 数据库兜底三层拦截。

二、电商三层防超卖完整技术防线(主流平台标准架构)


第一层:网关 & 前端流量拦截,从源头削减无效请求


这是第一道 “减压阀”,目的减少涌入库存系统的并发量,降低冲突概率。


[*]前端限流
秒杀按钮倒计时置灰、限制重复点击、增加滑块 / 验证码验证,拦截用户重复刷新、疯狂点击行为。
[*]网关动态限流(令牌桶 / 漏桶算法)
平台设定每秒最大处理请求数,超出阈值直接返回 “系统繁忙,请稍后再试”,主动丢弃超额流量。与其系统崩溃全部人抢不到,不如主动限制流量保证核心流程稳定,业内称为降级保护。
[*]风控防脚本
识别短时间高频 IP、批量账号请求,脚本流量直接拦截,避免机器占用真人抢购名额。

第二层:Redis 分布式缓存,秒杀核心防超卖屏障


数据库承载能力有限(单库每秒仅数千写请求),99% 的库存判断操作全部交给 Redis,依靠原子操作杜绝并发冲突。


[*]Lua 脚本原子扣减
不用分开 “查库存、减库存” 两步,将逻辑封装单条 Lua 脚本一次性执行,整个过程不可中断。多请求同时抵达时,Redis 串行执行脚本,不会出现同时读到同一库存的情况,单节点 Redis 可承载十万级 QPS。
[*]库存预热
活动开始前,将商品总库存提前同步写入 Redis,用户下单优先操作缓存,只有扣减成功的少量请求才会进入数据库。
[*]预锁定库存机制
用户下单瞬间 Redis 临时扣除库存,设置 5~15 分钟支付时效;超时未付款、支付失败,自动执行库存回补,释放名额给其他用户。

第三层:数据库兜底,最终一致性保障


Redis 仅做高速缓冲,MySQL 承担订单、库存永久存储,作为最后一道防超卖底线,提供两种数据库锁方案:


[*]悲观锁(排他锁 SELECT ... FOR UPDATE)
查询库存时锁定该行数据,同一时间仅一个事务修改,完全杜绝并发冲突。优点数据绝对准确;缺点高并发下大量请求排队,系统响应变慢,仅奢侈品、限量高价商品使用稀土掘金。
[*]乐观锁(版本号机制)
库存表新增 version 版本字段,扣减时校验版本号是否与查询时一致。若期间被其他订单修改,更新行数为 0,直接判定抢购失败。无需排队,吞吐更高,但高冲突场景大量用户会抢购失败,适合普通促销腾讯云。
[*]定时对账补偿
每小时 / 每日自动核对 Redis 缓存库存与数据库真实销量,数据不一致时自动修正,兜底极端宕机、网络异常导致的库存错乱。

中间缓冲:消息队列削峰填谷


Redis 扣减成功后,不会同步写入数据库,而是将下单消息送入 RocketMQ/RabbitMQ 消息队列。数据库按自身性能匀速消费,把瞬时洪峰流量拆分为平稳数据流,避免数据库被打崩,前端显示 “排队中” 就是请求进入队列等待处理。

三、明明显示抢到,订单却被系统自动取消的 6 大后台原因


1. 支付超时,库存自动释放


系统预扣库存后设置支付窗口期(通常 5-15 分钟),超过时间未完成付款,延迟队列自动触发库存回滚,订单关闭,名额重新投放市场。这是最常见取消原因。

2. 缓存与数据库同步失败(最终一致性回滚)


Redis 成功扣减库存,但消息队列消费异常、数据库宕机,订单无法持久落地。后台定时任务检测数据不一致,会撤销缓存扣减记录,订单同步取消。

3. 风控判定为脚本 / 恶意刷单


短时间高频请求、批量账号、代理 IP 下单,风控系统判定为抢购脚本,直接拦截订单并返还库存,防止机器抢占普通用户名额。

4. 商家线下实际库存不足


后台配置线上库存 100 件,但仓库实物仅 95 件。大量订单生成后商家无法发货,平台统一批量取消超额订单并赔付。

5. 重复下单、限购拦截


商品设置每人限购 1 件,同一用户多次提交抢购,第二次及之后订单会被系统自动作废。

6. 支付渠道异常


微信 / 支付宝扣款失败、余额不足、支付超时回调,系统判定交易未完成,释放库存关闭订单。

四、用户常见疑问通俗解答



[*]为什么页面显示有库存,点击却提示售罄?
页面库存是定时缓存数据,存在几秒延迟;Redis 实时库存已被抢空,缓存未同步刷新,造成视觉差。同时网关限流会直接拦截超额请求,即使库存有余,流量超限也会返回繁忙。
[*]系统为什么宁可取消用户订单,也不允许超卖?
超卖带来的赔付、客诉、监管处罚成本,远高于用户取消订单的体验损失。电商系统底层优先级:库存准确>用户抢购体验。
[*]悲观锁、乐观锁、Redis 哪个防超卖最好?
[*]少量限量高价商品:数据库悲观锁,保证绝对准确;
[*]日常中小促销:数据库乐观锁;
[*]大促百万级秒杀:Redis Lua 原子扣减 + 消息队列 + 数据库三层兜底,兼顾性能与准确。

文末总结


秒杀不超卖,是一套从前端、网关、缓存、消息队列到数据库的全链路防护工程,核心思路是层层过滤流量、原子操作锁库存、异步削峰、定时对账兜底。

抢到订单又被取消,并非系统 bug,大多是支付超时、风控拦截、数据同步异常触发的库存回滚机制。平台所有技术设计都围绕一个底线:无论并发多高,出库数量必须严格等于真实库存,杜绝超卖带来的经营风险。


页: [1]
查看完整版本: 秒杀百万并发不超卖!拆解电商三层库存防护体系,看懂抢到订单又被取消的底层真相