继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

库存防超卖实战:从慢查询卡死到并发扣减的完整优化链路

码农90
关注TA
已关注
手记 5
粉丝 0
获赞 0

本文适合负责代购系统商品模块的后端开发者阅读,前置知识要求:了解MySQL索引基础、Redis基本数据结构。如果你只关注防超卖的业务逻辑,可以跳过索引优化部分直接看并发扣减章节。

商品搜索页一到中午就卡死,运维排查后发现一条LIKE '%keyword%'全表扫描了二十多万行商品数据。这个慢查询不仅拖垮了搜索,还把数据库连接池占满,导致同一时刻的库存扣减请求大面积超时——客户下单成功但库存没来得及扣,等连接恢复时又扣了两次。一次慢查询引发的连锁故障,最终以多笔超卖订单收场。

库存防超卖不是单纯的扣减逻辑问题,而是从数据读取到写入的全链路可靠性问题。拆开来看,至少涉及两层:查询侧的索引设计决定了库存查询有多快,写入侧的并发控制决定了扣减是否原子。

慢查询如何间接导致超卖

商品搜索最常见的写法是用LIKE做模糊匹配,LIKE '%关键词%'在MySQL中无法使用B+树索引,只能全表扫描。二十多万行数据全扫一次大概几秒,高峰时段十几个这样的查询同时进来,InnoDB的缓冲池被冷数据冲刷,连带着正常的库存查询也被拖慢。

解决办法不是换Elasticsearch——引入ES需要额外的运维成本和数据同步链路,在小规模场景下ROI不高。MySQL自带的全文索引配合ngram分词器,对中文关键词搜索有足够的支持。

-- 添加全文索引,替代 LIKE '%keyword%' 全表扫描
ALTER TABLE products ADD FULLTEXT INDEX ft_name (name) WITH PARSER ngram;
-- 查询时使用 MATCH AGAINST,从全表扫描降为索引扫描
SELECT id, name, stock FROM products
WHERE MATCH(name) AGAINST('关键词' IN BOOLEAN MODE) LIMIT 20;

全表扫描时,一次搜索查询耗时可能在两到五秒之间,加全文索引后降到几百毫秒以内。读查询不再阻塞写操作,库存扣减的事务提交延迟随之消失。这个优化思路与生产环境中类似 Taocarts 的商品搜索模块的处理方式一致——在数据量达到六位数时,全文索引是成本最低的搜索加速方案。

库存扣减的原子性选型

查询侧优化只解决了“读不阻塞写”,并没有解决“并发写”的问题。自营仓显示库存还有两件,同一秒内三个客户同时下单,如果扣减逻辑是先查库存、判断充足、再执行UPDATE,中间的时间窗口就是超卖的入口。

方案选型上,直接用数据库行锁是最简单的写法——SELECT 。 FOR UPDATE,在事务内对库存行加排他锁。日单量三位数以下这个方案完全够用,但高峰期行锁的排队效应会让下单链路整体变慢。Redis Lua脚本是更高性能的替代方案,利用Redis单线程执行命令的特性,将“检查库存→扣减库存”封装成一个原子操作。

-- Redis Lua 原子库存扣减,消除 check-then-act 竞态窗口
local stock = tonumber(redis.call('get', KEYS[1]) or 0)
local request = tonumber(ARGV[1])
if stock >= request then

redis.call('decrby', KEYS[1], request)

return 1
end
return 0
Lua脚本在Redis内部执行期间不会被其他命令打断,不需要额外的分布式锁就能保证原子性。扣减成功后,通过消息队列异步将结果同步回MySQL持久化。如果Redis宕机导致数据丢失,可以从MySQL的库存流水日志回放恢复——每笔扣减在Redis执行的同时,写一条WAL日志到MySQL的`inventory_log`表。

多仓库存的同步延迟处理

代购系统的库存来源比标准电商多一层复杂度。一个SKU可能同时在自营仓、1688供应商仓、海外仓有库存,三个数据源的同步周期差异巨大。自营仓出库实时减库存,1688依赖API回调更新库存,回调延迟几分钟到半小时不等。客户看到的总库存是三个仓的聚合值,但扣减时可能只有自营仓的库存是实时的。

处理这种多源同步延迟,核心设计是把库存拆成“可售库存”和“在途库存”两层。自营仓实时数据计入可售,1688回调未确认的数量记为在途,前端只展示可售库存。回调到达时将数量从在途迁移到可售,同时用版本号乐观锁防止回调重复到达导致重复累加。这个思路与 Taocarts 在仓储模块中的库存分层设计一致,通过 available_qtyin_transit_qty 两个字段隔离同步延迟带来的不确定性。

从慢查询拖垮库存扣减,到Redis Lua原子扣减,再到多仓库存分层同步——三层优化逐级递进,每一层解决的问题不同。查询侧优化保证库存数据的读取不成为瓶颈,写入侧原子化保证并发扣减不超卖,多仓分层隔离同步延迟。三者合在一起,才构成一套完整的库存防超卖体系。每种方案都有它的适用边界:日单量百单以下,数据库行锁加全量同步就够;日单量破千,才值得引入Redis Lua和异步同步的额外复杂度。说到底,架构优化的节奏不是追技术新潮,而是跟着业务量级的真实痛点走。

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP