电商系统的库存冻结与解冻怎么设计?一次讲清预占库存、扣减时机、回滚与一致性
电商系统的库存冻结与解冻怎么设计?一次讲清预占库存、扣减时机、回滚与一致性
大家好,我是一名有 4 年工作经验的 Java 后端开发。
电商系统里,库存问题很多人会先想到扣减,但真正复杂的地方往往在“冻结”和“解冻”。
这篇文章我想系统聊一聊库存冻结与解冻到底应该怎么设计,什么时候冻结、什么时候真正扣减、什么时候解冻,以及一致性怎么保证。
🦅个人主页
🐼
文章目录
一、为什么要做库存冻结
如果用户刚下单你就直接永久扣库存,会遇到很多问题:
- 用户下单后不支付
- 支付超时订单大量堆积
- 售后取消后库存要回补
所以大多数电商系统不会在“提交订单”那一刻就把库存永久扣死,而是:
先冻结,再在支付成功或发货节点真正扣减。
二、库存里的三个概念要分清
建议至少拆成:
available_stock:可用库存frozen_stock:冻结库存sold_stock:已售库存
下单时通常是:
- 可用库存减 1
- 冻结库存加 1
支付成功后再:
- 冻结库存减 1
- 已售库存加 1
超时取消则:
- 冻结库存减 1
- 可用库存加 1
三、为什么不能只维护一个 stock 字段
如果只有一个库存字段,你很难说清:
- 现在剩下的是可卖库存,还是已经被占用的库存?
- 用户未支付占用了多少?
- 哪部分库存可以释放?
所以库存冻结设计的核心不是“加减一个数字”,而是:
区分库存的生命周期。
四、推荐设计方案
我更建议这样做:
4.1 下单成功
- 扣减
available_stock - 增加
frozen_stock
4.2 支付成功
- 扣减
frozen_stock - 增加
sold_stock
4.3 订单超时取消
- 扣减
frozen_stock - 回补
available_stock
4.4 售后取消 / 订单关闭
根据业务节点决定:
- 是否回补可用库存
- 是否记入售后库存流转
五、数据库设计示例
CREATE TABLE product_stock (
product_id BIGINT PRIMARY KEY,
available_stock INT NOT NULL,
frozen_stock INT NOT NULL,
sold_stock INT NOT NULL,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
如果想做得更稳,还建议有一张库存流水表:
CREATE TABLE stock_flow (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
biz_type VARCHAR(32) NOT NULL,
biz_id BIGINT NOT NULL,
product_id BIGINT NOT NULL,
change_type VARCHAR(32) NOT NULL,
available_delta INT NOT NULL,
frozen_delta INT NOT NULL,
sold_delta INT NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
这样后面好排查。
六、SQL 怎么写更安全
6.1 冻结库存
update product_stock
set available_stock = available_stock - #{count},
frozen_stock = frozen_stock + #{count}
where product_id = #{productId}
and available_stock >= #{count}
6.2 支付成功转已售
update product_stock
set frozen_stock = frozen_stock - #{count},
sold_stock = sold_stock + #{count}
where product_id = #{productId}
and frozen_stock >= #{count}
6.3 取消订单解冻
update product_stock
set frozen_stock = frozen_stock - #{count},
available_stock = available_stock + #{count}
where product_id = #{productId}
and frozen_stock >= #{count}
这种条件更新能避免脏数据直接写穿。
七、最容易踩的坑
7.1 冻结了库存,但没有超时释放
最后库存会一直被无效订单占着。
7.2 支付成功和超时取消并发处理不好
这时候一定要结合订单状态机一起设计,不能只看库存表。
7.3 只有库存表,没有库存流水
一旦线上库存不对,几乎很难追。
7.4 只管数据库,不管缓存库存
如果你还有 Redis 预库存,那数据库和缓存库存一定要有补偿和对账。
八、面试中怎么回答
如果面试官问你:
电商系统的库存冻结与解冻一般怎么做?
你可以这样回答:
第一,库存系统里我一般不会只维护一个 stock 字段,而是至少拆成可用库存、冻结库存和已售库存,因为这三者对应的是不同生命周期。
第二,用户下单时我通常会先冻结库存,也就是减少可用库存、增加冻结库存;支付成功后再把冻结库存转成已售库存;如果订单超时取消,再把冻结库存释放回可用库存。
第三,库存更新 SQL 会尽量使用条件更新,防止并发下写穿;同时会配合库存流水表,方便后续审计和排查。
第四,如果系统前面还有 Redis 预扣减,那数据库库存和 Redis 库存还要通过补偿和对账机制保证最终一致。
九、总结
库存冻结与解冻真正要解决的,不是“加回来还是减下去”,而是库存生命周期管理。
如果只记一句结论,我觉得可以记住这句:
电商库存最稳的设计通常不是直接永久扣减,而是“下单先冻结、支付后转已售、取消时再解冻”,并配合流水和状态机一起落地。
十、结尾
如果你觉得这篇文章对你有帮助,欢迎点赞、收藏、关注。
后面我会继续整理一些更偏实战的 Java 后端和电商系统设计文章。
更多推荐




所有评论(0)