做了十年电商库存系统,我见过太多运营在活动开始前半小时才跑来问:“快帮我看看,这个活动SKU怎么设置库存锁定?要不又要超卖了。”而通常这个时候,代码已经上线,活动页已经放出去,能做的事只剩下祈祷。
真正让我想写这篇文章的,是去年618期间的一个真实案例:某品牌旗舰店上了一款引流款SKU,库存只备了2000件,但在预售开启后的12分钟内,后台订单显示成交了3150单。超卖1150件。运营主管第一反应是“系统有Bug”,技术查了一晚上发现,问题根本不在并发,而在“库存锁定”这个动作从一开始就没有发生,他们把活动SKU挂在了普通商品池里,走的是SPU级的总库存扣减,而总库存早已被另一条非活动渠道占用了。
这1150件超卖订单,最终让品牌方赔付了3.8万元无门槛券,外加50%的差评率。复盘会上,技术负责人说了一句让我印象极深的话:“我们不是不会写锁库存的代码,我们是从一开始就没搞清楚‘锁定’到底要解决什么问题。”
这篇文章,我想把“活动专属SKU库存锁定”这件事讲透。不单讲技术方案,也不单讲运营操作,而是把它们放在一起,讲清楚:什么时候该锁,锁多深,锁多久,什么时候必须释放,以及锁错了要付出什么代价。
做活动专属SKU的库存锁定,首先不是写代码,而是要回答三个业务问题:
我发现一个规律:凡是这三个问题回答得不清楚的团队,无论用什么技术框架,都会出问题。有的团队用了很高级的Redis分布式锁,但业务上没想明白“锁多久”,导致高价值SKU被大量低意向用户白白占用,活动转化率反而下降了。
从业这么多年,我把防超卖拆成四层防线,每一层解决一类问题:
第一层:库存扣减的原子性。这是代码层面的保障。多线程、多实例同时扣减库存时,要保证不出现“库存为负数”或“两边同时成功”的情况。常见做法是数据库行锁、乐观锁或Redis原子操作。这一层的目标是保证单个订单不会因为并发而买到不存在的货。
第二层:库存锁定的业务规则。这是策略层面的保障。哪些SKU需要锁定,锁定的数量是多少,锁定时间多长,提前预留多少库存给高净值用户,都需要在活动开始前以配置形式确定下来。这一层决定了你是在“防超卖”的同时还能保证转化,还是在“防超卖”的同时把订单也防没了。
第三层:多渠道库存的协调。如果你的同一款SKU既在淘宝卖,又在京东卖,还同步到了线下门店,那么“活动专属库存”必须和“共享库存池”做隔离。否则活动流量一冲,先把共享池冲穿,其他渠道的订单全部超卖。
第四层:超卖后的即时补偿。写这一层可能有人会觉得晦气,但我在系统设计评审时始终强调:零超卖是一种理想态,不是一种承诺态。万一超卖了,有没有一套自动化的补偿SOP,决定了用户是把你的品牌骂上热搜,还是默默接受退款和折扣券。这四层缺一不可。你可以只做第一层加第二层就上线一个简单活动,但只要你的业务规模和流量一旦上来,后面两层会变成你的保命牌。
每次有朋友让我帮忙看他们的库存系统,我不会先看代码,而是先问三个数据:
这三个数据比任何技术架构图都更能反映系统的健康程度。我见过小电商公司没有任何高级中间件,仅靠数据库事务就把超卖率控制在万分之一以内;也见过花了重金买了全套高可用架构的品牌,因为锁定时长被拍脑袋设成30分钟,导致大促期间核心SKU支付转化率不到40%。技术只是工具,决定成败的永远是业务判断。
下面这张图是一个典型促销活动中“锁定时长、支付转化率、超卖率”三者的关系。我从多个商家的后台数据中总结了规律,数值属于范围级参考,不是精确统计,但趋势高度一致:

在我的经验里,活动专属SKU其实分三种,它们的锁定策略完全不同:
第一种:活动专供款。货只做了一批,只在线上的这个活动中卖。这种情况下,库存锁定最简单,因为是独占库存池,不存在多渠道打架的问题。你需要处理的只是并发和防止超卖。
第二种:活动专属SKU = 老款重新包装。商品本身在其他渠道也在卖,但为了活动,单独拆出了一个SKU编号。这种“身份分离,物理同仓”的SKU,最需要警惕的是人工审单、仓库配货时拿错库存,以及两套SKU之间的库存同步延迟。
第三种:活动专属SKU + 渠道专供组合。比如某个SKU只在抖音直播间卖,同时这个商品还开了一个天猫的日常销售链接,两个链接共享同一个物理库存池,但需要在不同的库存上限下做隔离设定。这种是最复杂的场景,一旦运营在后台只给“总库存”设了一个数字,而没有单独设置“活动库存上限”,超卖随时可能发生。
你可能觉得,一个每秒几百QPS的小活动,有必要研究库存锁定吗?我直接给你看一组我接触过的商家后台流量数据。某服饰商家在中腰部主播的直播间上架了一款秒杀SKU,库存500件。开播后第10分钟,这个商品的详情页和加购接口被同时打入8000多个请求。虽然最终只有几百人成功支付,但系统在那一瞬间接收到的并发请求量是平时峰值的20倍以上。
这种场景下,如果库存锁定逻辑是“用户点击立即购买时,先查询数据库,再判断是否可以锁定”,那么一秒内几千个请求同时压到数据库,结果就是两个:一是数据库连接池被打满,页面报错;二是大量请求同时读到“库存还剩100件”,然后一起走到扣减逻辑,其中一部分因为数据库的乐观锁冲突而失败,另一部分则成功超卖。你回头去看数据,会看到“扣减请求数”远大于“库存初始化数”,但是你不一定能马上意识到这就是锁的粒度问题。

很多运营把“防超卖”理解成“不要在页面上卖超出库存的数量”。这句话只说对了一半。真正让商家头疼的,是超卖之后的连锁反应。我举一个典型的连锁链条:超卖 → 部分订单无法发货 → 平台判定延迟发货 → 店铺扣分 → 流量权重下降 → 后续一周的自然流量减少30%。这个链条里,超卖本身的赔付成本往往不是大头,因店铺权重下降造成的流量损失才是。
所以我一直强调:库存锁定要做的不是“等超卖发生时兜住”,而是在活动开始前,就把可能引发超卖的所有入口都堵上。这里包括:预售渠道、购物车合并结算、多渠道库存共享、员工内部购买通道甚至黄牛脚本。我见过一个案例,品牌的库存锁定在技术上毫无问题,但运营在设置活动时把“员工内购通道”的库存上限给忘了,结果内购流量把200件库存全部买走,活动商品比原计划提前四个小时下架。这个锅最终也记在“库存锁定不合理”上,虽然从系统层面看,每一笔订单都锁住了。
我接手过的不少项目,库存锁定模块写得很漂亮,但问运营“为什么要锁30分钟”,得到的回答往往是:“系统默认值,一直没改过。”这是最典型的一种错误:把锁定时长当成了技术参数,而不是业务策略。
我观察过不同类目的用户行为,差异非常大。服装类目的用户,平均决策时间在8-12分钟,他们会反复切换SKU颜色和尺码;3C数码高单价商品,用户决策时间往往超过30分钟,他们可能会去其他平台比价;而生鲜、快消类目,用户从看到商品到下单通常不超过3分钟,如果让一个生鲜商品锁库存30分钟,那基本等于告诉其他用户“这个菜已经被抢了”,转化率会直线下降。
有些品牌,后台是多年以前的老系统,只有“商品总库存”这一层概念。运营想做活动,就在总库存上划走500件作为活动库存。听起来没什么问题,但实际运行起来会出现一种情况:一个SKU(比如红色M码)被锁定了200件,另一个SKU(红色L码)被买光了,但系统显示总库存还有货,用户下单之后却被通知“缺货”。这种“部分SKU可卖,总库存却乱套”的情况,就是锁定粒度不够导致的。
SKU级锁定看起来多花了很多配置时间,但它是唯一能保证“用户在付款前看到的信息是真实可用库存”的方案。如果你的系统只能做SPU级,那活动专属SKU这一点上,你已经输了第一步。
这是我在技术圈看到的最具误导性的说法。Redis的原子操作确实能保证并发扣减不错乱,但“Redis扣减成功”和“订单真正有货”是两回事。我见过不止一个项目,缓存里扣减了库存,数据库却没同步上;或者数据库扣减了,但Redis因为数据一致性延迟,又把库存“加”回去了。最后的结果是:用户看到有货,支付后系统确认无货,还是超卖。
Redis作为库存扣减的入口是可行的,但必须配合“异步对账”和“数据库最终扣减”。缓存只承担限流和快速判断,真正决定订单能不能成立的,永远要是数据库里那行库存数据的最终状态。
活动中往往既有引流SKU,又有利润SKU,还有清仓SKU。它们的角色完全不同。引流SKU的目的是拉新、吸引点击,你对它的锁定策略应该偏宽松,锁定时间短、释放快,让更多用户有机会看到;利润SKU的目的是成交,可以对高意向用户适当延长锁定,但对普通用户保持严格限制;清仓SKU的目的是去库存,锁定时间可以短一点,甚至不锁定,因为清仓场景下,超卖的风险远小于库存积压的风险。

锁库存只是过程,不是终点。很多后台从活动开始后就一直高亮显示“库存被锁定中”,但系统没有任何清理逻辑。这些锁定记录可能是用户早就放弃了支付,也可能是用户已经关闭了页面,但库存还占在那里。一个周五晚上开启的活动,如果锁定清理脚本只在每天凌晨执行,那一整个周末的黄金转化时间段都在被无效锁定拖累。
清理方案至少应该包含三类:超时未支付自动解锁、用户主动取消订单后立即释放、风控识别到异常订单后强制释放。这三点做得越及时,库存的“水分”就越少。
判断“锁定到哪一层”,不能只看系统能力,还要看活动目标和商品属性。我建议遵循以下原则:
如果商品有多种SKU,但都是同一个生产批次、同一个包装、同一个仓发货,那么锁定到SPU即可。例如一箱水、一袋米,不同SKU只是规格描述差异,实际物理库存是同一堆货。这时候拆成SKU级锁定反而会制造虚假的“缺货”假象。
如果商品SKU之间可能形成替换关系,比如颜色有差异、尺码有差异、容量有差异,必须锁定到SKU级。因为用户对颜色尺码有强烈的偏好,替换性很差。你把蓝M码锁了,蓝S码和黑M码都还有库存,但用户大概率不买。
如果是多仓发货,还需要在原SKU级之上叠加“仓维度”的锁定逻辑。比如华东仓库存5件、华南仓库存5件,用户地址在华东,那么他只能看到华东仓的5件。如果不能做到仓维度锁定,就会出现“订单归属到华南仓,但华南仓已经没货”的情况。

关于扣减时机,我的观察是:纯“支付扣减”在活动场景下几乎不可用,纯“下单扣减”在低转化类目下又太浪费,最优解一般是两者的中间态。
所谓“中间态”,就是用户在点击“提交订单”时,系统先冻结一部分库存,但不是扣减;支付成功后,再执行真实扣减;支付超时,自动解冻。这个策略兼顾了三方诉求:用户下了单,库存就有保障,不会发生“支付时发现没货”;运营也不会因为大量未支付订单把库存锁死,因为超时后会释放;财务上,扣减时间和支付时间保持一致,不会出现“已扣减但未支付”的账实不一致。
关于“预锁数量”,我有一个经验值可供你参考:如果活动的预估支付转化率在60%-70%之间,冻结库存的数量可以设置为活动可售库存的1.6至1.8倍。也就是说,你想卖1000件,系统允许冻结的上限是1600-1800件,但冻结的前提是最早冻结的订单开始超时释放。这个倍数不能拍脑袋,需要根据你往期活动的支付转化率做动态调整。如果转化率偏低,可以适当上调倍数;如果转化率高,则下调。
比“锁库存”更重要的是“释放库存”。我把释放时机分成三类:
(1)超时释放。超过设定时间(比如15分钟)未支付,自动释放。关键在于设定多长。我建议根据商品客单价来分级:低客单价(100元以下)锁5-10分钟,中客单价(100-500元)锁15分钟,高客单价(500元以上)可以锁30分钟。高客单价的用户需要更多时间做支付决策,适当延长锁定能够提升转化。
(2)主动释放。用户主动取消订单、关闭购物车、切换了配送地址时,系统应该立刻释放对应SKU的锁定库存。这个动作要做得越及时越好,最好能做到秒级释放。但很多电商后台做不到,因为订单取消消息要经过队列、状态机、异步通知,等库存真正释放时已经过了几分钟。
(3)异常释放。风控拦截、反作弊引擎识别到异常订单时,库存也要自动释放。这里的难点在于判断策略:如果释放太快,可能会误伤正常用户;如果释放太慢,黄牛可能已经把库存全部占住。
锁定时长到底是5分钟还是15分钟?我建议不要靠拍脑袋,而是去看自己店铺的“放弃支付时间分布”。我经常给商家的建议是:从后台拉出过去30天所有“提交订单但未支付”的数据,看它们在时间轴上的分布。如果大多数用户在第3分钟放弃支付,说明你的产品详情和价格传递效率高,5分钟锁定就够了;如果相当一部分用户在第12分钟才放弃支付,说明用户需要更多时间评估,15分钟锁定更合理。用数据去校准锁定时长,而不是用默认值,这是成本最低、见效最快的优化手段。
这个品牌是中等规模,年销过亿。他们之前遇到的问题是:大促活动开启后,核心爆款SKU在10分钟内被锁定400件。但最终支付转化率只有44%,也就是说有一半以上的库存被“假意向”用户占住了。而这些被占住的库存,又导致真正想要的用户在下单时看到缺货,转身去了竞对店铺。
我们做的调整是:引入会员分级锁定。高等级会员(年度消费TOP20%)锁定时间延长到30分钟,普通会员锁定时间控制在10分钟;同时,被释放的库存优先进入“可被高等级会员再次锁定”的状态,而不是回到公共池。调整之后,这批爆款SKU的支付转化率从44%提升到了69%,超卖率没有上升,反而因为高等级会员的转化确定性更强,超卖风险进一步下降了。
这个案例给我们的启发是:锁定策略不是“无差别”的,它可以向高价值用户倾斜。同样的库存,在不同用户身上的“成交概率”是不同的。把库存锁定给更可能付款的人,是最高级的防超卖技巧。
这是一家做手机配件的商家,曾经在大促时遇到过严重的超卖。他们的老系统是每次请求都直接操作数据库,峰值时数据库CPU直接打满,出现了几十个超卖订单。后来我们做了调整:活动开始前,把活动SKU的库存预热到Redis里;用户请求进来时,先走Redis的原子扣减,扣减成功后再异步写回数据库。同时,数据库里保留一个unique key(订单号+SKU),保证同一笔订单不会重复占用库存。
这套方案上线后,他们曾经在某个平台大促中扛过了3万QPS的峰值,没有发生一笔超卖。但我必须强调,这并不代表Redis是万能的。Redis预热方案能否成功,取决于三个前提:第一,Redis本身的部署要足够稳定,不能用单点;第二,Redis到数据库的异步对账需要有幂等机制,否则重复同步会造成数据错乱;第三,必须有一个兜底脚本,活动结束后逐笔核对Redis和数据库的最终一致性。我把他们的核心逻辑简化如下:
活动开始前:
1. 将SKU可售库存加载至Redis,设置过期时间
2. 初始化数据库库存字段与Redis一致
用户提交订单时:
1. Redis原子扣减:DECR sku_stock_{skuId}
2. 若扣减后值 = 0,生成预占单号,异步发送到MQ
异步消费MQ:
1. INSERT INTO order_sku_lock (order_no, sku_id, status)
2. 通过唯一索引保证同一订单号只会扣减一次
3. UPDATE db_sku_stock SET stock = stock - 1 WHERE sku_id = ? AND stock > 0支付完成回调:
1. 更新订单状态为已支付
2. 释放未支付订单对应的Redis库存(若有)
这段逻辑本质上还是在回答我们前面说的三个问题:锁多细(SKU)、锁多久(Redis的过期时间和订单超时时间)、锁了之后做什么(异步对账)。
母婴类目的一个特点:用户下单后不会立刻支付,因为很多用户习惯在购物车里凑单、比价,或者等到晚上统一结算。这家母婴店原本的扣减逻辑是“支付成功后扣库存”,结果活动期间用户提交订单后去支付,却提示“库存不足”。用户当然不理解,直接投诉+差评。
后来我们改成了“下单冻结+支付确认”的模式。用户在提交订单时,系统先冻结库存15分钟;支付成功后自动转为已扣减状态;15分钟未支付则释放。这个改动让这家母婴店的支付环节报错率接近于零,同时因为库存能在15分钟之后释放,实际被浪费的库存占比从原来的35%降到了18%左右。
这里我要分享一个观察:越是客单价中等、决策时间长的类目,越需要使用“下单冻结+支付确认”的模式。它既解决了“用户支付时没货”的体验问题,又不会因为锁库存太久而放过太多低意向的流量。

这个阶段的卖家,通常用的是平台自带的后台,或者一套简单的ERP系统。你不需要去搭建Redis集群,也不需要搞分布式锁。你真正要做的是:
你已经有几个稳定出单的SKU,也具备一定的技术或运营能力。这时候你应该从“手工对表”往“系统配置”升级。我的建议是:
到这个规模,你的系统必须能扛住高并发,并且能处理好跨渠道、跨仓、跨库存类型的逻辑。我建议:

我把这个表格称为“防超卖的底线”。哪怕是技术架构再完善,你依然需要一个人用表格去复核系统的结果。表格不需要复杂,三列就够:
| SKU | 锁定中库存 | 释放原因 |
|---|---|---|
| A01-红色-M | 32 | 超时未支付 / 用户取消 / 风控拦截 |
| A01-红色-L | 18 | 超时未支付 |
| A02-蓝色-S | 5 | 用户取消 |
每天活动结束后,导出系统数据,人工复核锁定中的库存是否都对应有效的未支付订单。如果出现“锁定库存=3,但实际未支付订单只有1笔”的情况,说明系统里有死锁或脏数据,需要当天处理。很多超卖问题不是发生在峰值那一秒,而是活动结束后第二天,对账时才发现有一批订单不能被满足。这个习惯能让你在问题发酵之前就堵住漏洞。
这是我反复强调的一组矛盾:锁得越久,库存越安全,但真正想买的人可能因为看到缺货而流失。用户不会因为你锁库存而等你,他只会看到“可售库存一直在减少”这个信号,然后决定是赶紧下单,还是去别家看看。
根据我对多个店铺的观察,锁定时长从15分钟改到5分钟,支付转化率平均提升3%-5%,但超卖率会从“极低”变成“依然可控”。对大多数中小卖家来说,我更建议把锁定时长往短了设,因为你的流量本来就有限,与其让低意向用户占着库存,不如把库存放给下一个真正想买的人。
如果你要求“库存数据在任何时刻都绝对一致”,那么理论上你要付出极高的系统代价。每一次库存变化都要加锁、事务、同步等待,并发能力会被严重拖累。在大促场景下,这几乎不可能做到。所以你需要做取舍:用“最终一致性”替代“实时一致性”。用户在页面上看到“仅有3件”和实际可售库存之间可以有几秒的差距,但在点击“立即购买”的那一刻,系统必须做出准确的判断。也就是说:查询可以异步,扣减必须原子;展示可以近似,成交必须精确。
把库存锁定做到“SKU+渠道+仓库+会员等级”级别的精细度,系统的安全性和库存利用率都会很高,但运营每次上活动也要付出更多配置成本。你要为每个SKU分别设置锁定数量,为每个渠道设置库存上限,为每个会员等级设置不同的释放时间。如果团队只有一个人负责,这可能会成为新的瓶颈。我建议从“SKU+活动库存池”开始,先把最基础的一层做对,再去叠加渠道和会员等级,不要一上来就追求大而全。

最后说一个容易被忽略的取舍:近年来各大平台都在强调“确定性”,比如商品页显示“现货,24小时内发货”,或者“库存紧张、仅剩X件”。这类玩法的核心在于你要敢于释放库存信号。如果你的锁定策略过于保守,系统永远显示“库存不足”,平台算法会判定你的商品缺少承接能力,反而减少推荐流量。所以,库存锁定不只是防御动作,它还会影响平台对你店铺的流量评估。适度的库存可见性,对公域流量的获取有正向作用。
库存锁定这件事,很多团队花了大量精力在“如何让系统不超卖”上,但我认为这只是及格线。真正值得投入精力的,是“如何在防超卖的同时,让真正想买的用户顺利完成购买”。这里面既有技术架构的问题,也有数据策略和业务流程的问题。
如果只记住三个要点,我希望是:
下一步,你可以做三件事。第一,打开后台,看看你现在每个活动的SKU锁定期限是设置了多少,是谁设置的,依据是什么。第二,拉一份过去30天“提交订单但未支付”的数据,画一下时间分布,判断你的锁定期限是否合理。第三,把今天文章里提到的“库存锁定对账表”存下来,下次活动后花20分钟做一次人工复核。这三件事都不需要写代码,但它们能让你在“防超卖”这件事上,超过80%的同行。
我之前做618大促,把SKU库存锁定时间设成30分钟,结果大量用户锁了库存后去别家比价,最后不付款,等释放出来已经错过最佳转化时机。还有用户回头发现没货了投诉我们虚假宣传。到底锁多久才合理?怎么才能既防超卖又不损失订单?
先给结论:锁定窗口没有标准答案,只有决策框架。它不是技术参数,而是你给用户留的决策时间。锁短了,高客单用户没比完价就被释放,客诉上升;锁长了,低价冲动消费场景的库存被大量占着,真正想买的人买不到,平台流量被白白浪费。我踩过这个坑。
早年做服饰类目大促,全店SKU统一锁15分钟,结果出现两个极端:高客单大衣的用户还在比价,库存就被释放了;另一边9.9元引流款的库存被黄牛脚本秒拍锁住,真实用户拍不到。之后我把两类SKU拆开:引流款锁5分钟,高客单锁30分钟,客诉和超卖同时下降。
给你一份按品类估算的参考表(经验值,需结合自身数据校准): 品类 | 建议锁定窗口 | 判断依据 低价冲动消费品(小于50元) | 3-5分钟 | 决策链路短,锁久只给黄牛留窗口 服饰鞋包(50-500元) | 5-15分钟 | 用户大概率会对比同款,但不会逛太久 高客单3C与家电(大于1000元) | 30-60分钟 | 需要查参数、问客服、比价,锁太短等于没锁 大额券与预售专属 | 60-120分钟 | 用户已付出凑单成本,锁定时间应覆盖真实犹豫期 比窗口更重要的,是释放后的补偿动作。
用户锁的库存被释放后,系统应该立刻推送一条“你关注的商品库存仍在”的召回消息,配合一张小额券。这个动作能把释放库存的二次转化率拉高15%-30%。最后提醒一个反直觉的坑:很多系统在锁定期满自动释放时,并不会马上把库存写回可售池,而是等一个延迟任务。大促期间这个延迟可能长达10分钟。
也就是说,你看到后台“已释放”,前台仍然显示无货。所以活动结束后,务必人工核对“锁定量、释放量、可售量”三个数字。
我们开发说支付减库存成本高做不了,只能做下单减库存。可我又怕被恶意锁库存或者用户大批量退款导致库存虚占。身边人有的说预扣好有的说实扣好,我该怎么判断?
先亮观点:纯下单减库存和纯支付减库存都有致命缺陷,活动专属SKU更应该做的是“锁定中间态”。也就是用户下单时只冻结一个“库存资格”,支付成功后才真正扣减库存。这个资格本身也分有效期,过期自动解除冻结。为方便决策,我把两种主流模式拆开对比: 下单减库存(预扣) 优点:实现简单,不会超卖;
缺点:容易被恶意拍下占用库存;退款单会长时间挂着库存;适用:低并发、熟人信任制、社群团购场景。支付减库存(实扣) 优点:资金流和库存流一致,库存利用率高;缺点:支付回调有延迟,高并发下库存扣减不及时,反而更容易超卖;适用:技术团队能力强、有异步消息队列和重试机制的平台。我亲身踩过预扣的坑。
某次活动上架了3000件高毛利SKU,被脚本批量下单但全部不付款,库存占满6小时。真实买家进店看到“可购买”,拍下后却一直显示待付款占着库存。最后人工砍单1800单,赔了一批无门槛券,活动毛利被吃掉一半。如果技术团队做不了支付减库存,可以用“预扣+三层防护”替代。第一层,限购规则。
同一用户限购5件,同一地址限购10件,这是成本最低的拦截手段,能直接挡掉九成以上的脚本用户。第二层,风控黑名单。识别同一设备号、同一下单人频繁锁单不付款的情况,直接拉黑。建议在活动前先跑一遍历史订单,把已知风险账号提前加黑。第三层,库存对账任务。
每15分钟同步一次“已锁定未支付”的订单清单,批量释放超时订单。这个任务不需要复杂系统,写个定时脚本即可。我们上线这套组合后,恶意锁单量下降了约70%。最后补充“锁定中间态”的落地姿势:下单时创建一条“锁定单”,状态为待支付;支付回调成功后,把锁定单转成正式订单并扣减库存;
如果超时未支付,关闭锁定单并释放库存。开发成本并不比纯支付减库存高太多,但业务容错性明显更好。
看技术文章都说Redis做库存锁定是秒杀标配,但我总感觉不踏实,Redis挂了怎么办、缓存和数据库数据不一致怎么办?我们项目用的是Redis扣减,数据库才是最终库存源,这两者到底怎么配合才安全?
先说我的结论:Redis锁不是不能防止超卖的万灵药,它只能防住“正常并发下的超卖”,防不住“Redis本身故障导致的数据不一致”。如果你的系统把Redis当库存唯一数据源,那风险更大。因为Redis内存态的数据一旦丢失,前台展示的库存和数据库真实库存就对不上。
我经历过一次事故:大促峰值期间Redis主从切换,锁短暂失效,同时库存扣减日志还没来得及异步同步到数据库,最终超卖1200单。复盘时发现,核心问题不是Redis不够快,而是缓存与数据库之间缺一个最终一致性兜底。
三种方案的防超卖强度与适用量级,我做了个对比: 方案A:数据库条件更新(where stock大于0) 防超卖强度:强;并发上限:约500-1000 QPS;成本:最低;适用:中小商家、日常活动。方案B:Redis扣减+异步同步数据库 防超卖强度:中,取决于对账机制;
并发上限:2000-5000 QPS;成本:中;适用:体量较大、有专门研发维护的团队。方案C:分布式锁(如Redisson红锁)包住数据库操作 防超卖强度:强;并发上限:1000-3000 QPS(受锁粒度影响);成本:高;适用:强一致优先、接受一定性能损耗的场景。我的专家判断是:先看量级再选型。
QPS不超过500,直接用数据库条件更新,没必要引入Redis。超过2000,再考虑Redis扣减,但必须同时做三件事:每次扣减写一份操作流水;定时对账任务比对Redis与数据库的库存差;Redis不可用时自动降级回数据库条件更新,保证活动不直接失败。
另外,无论选哪种方案,都要在数据库层加一条唯一索引兜底,比如(订单号+SKU编码)。这是最后一道物理防线,即使前面的锁全部失效,数据库也能拒绝重复扣减记录。线上排查超卖问题时,这条索引能帮你直接定位到异常订单,而不是靠捞日志去猜。
我们运营为了做到零超卖,把活动SKU全部锁死,结果活动销量反而没达标。用户在详情页看到有货,加购物车后就是不付款,库存被锁住,真正想买的人又买不了。到底怎样才能既防超卖又不伤转化?
很多人把库存锁定单纯理解成“防超卖的技术开关”,但库存锁定的本质是一道营收取舍题。你锁住的不只是库存,还有一部分用户的下单冲动。我自己做过一次小范围对照:同款活动商品,A组锁定15分钟,B组锁定5分钟。活动结束后,B组的支付转化率比A组高了1.2个百分点,超卖率没有明显差异。
原因不难解释:锁定时间越长,用户越容易产生“我反正锁到了,先去别家比价”的心理,回来付款的比例反而下降。所以我的核心建议是:放弃一刀切,改做柔性锁定。三招可以落地。第一,分级锁定。高价值用户(历史消费高、会员等级高)锁30-60分钟,普通用户锁10分钟。
理由是,高价值用户的决策链路天然更长,但流失成本也高,值得给足时间;普通用户则更适合用短锁定制造“紧迫感”。第二,阶梯释放。锁定5分钟未支付,发一条站内提醒“库存正在为你保留”;10分钟未支付,推送一张限时券,券的过期时间设置成锁定过期前1分钟。这一步的逻辑是把“用户遗忘”拉回“比价现场”。
第三,动态库存池。把活动可用库存拆成“锁定池”和“机动池”。锁定池被占用过多时,机动池自动补位,保证前台永远有可售库存,避免“明明有货却买不到”的尴尬。
放一张对比表供你判断: 策略 | 支付转化率 | 超卖风险 | 客诉率 | 运维成本 统一锁定15分钟 | 基准 | 低 | 中 | 最低 统一锁定5分钟 | 基准+1.2% | 低 | 低 | 最低 分级+阶梯释放 | 基准+1.5%左右 | 中低 | 低 | 中 动态库存池+分级 | 基准+1.8%-2%(估算) | 中 | 低 | 高 最后提醒一句:柔性锁定非常吃客服话术。
用户收到“库存即将释放”的提醒后,如果来咨询,客服不能只回“系统自动的”,而要主动给一个台阶,比如“我帮您备注优先发货”。否则用户会觉得自己被系统逼单,反而直接流失。


读者评论
文章把四层防线讲得很清楚,特别是“锁多细、锁多久、锁了做什么”这三问,很多团队确实没想明白。我们之前只做了第一层,结果活动渠道和非活动渠道共享库存,导致超卖。现在按这个思路重新梳理,把锁定粒度细化到SKU加渠道,才真正解决问题。
最打动我的是锁定时长那组数据,5分钟和45分钟的支付转化率差了20多个百分点。以前我们一直用默认30分钟,结果高转化SKU被大量无效占用。现在我们把生鲜类目锁定时长改成3分钟,超卖率没涨,转化率反而升了。库存锁定真的要按类目调整。
文中“活动专属SKU=老款重新包装”的案例太真实了。我们之前就是只拆了SKU,但没隔离物理库存,导致仓库发错货。后来才明白“身份分离,物理同仓”的SKU必须做库存同步和标记。这篇文章把三类SKU说得很透,强烈推荐给同行。
很认同“Redis扣库存快不代表安全”这个观点。我们曾经用Redis做扣减,但业务上没定义好锁定时长,结果大量无效占用。实际上Redis只是工具,核心是锁定策略和业务规则。文章里提到的三问才是根本,技术实现反而其次。
去年大促超卖1150件,赔了钱还掉权重,早看到这篇就好了。尤其是超卖后的补偿SOP,我们当时就是临时发券,用户根本不买账。现在把自动化补偿和库存预警都加了,第四层防线必须要有。作者讲得很实在,不是空谈理论。