两件库存的商品在十分钟里卖出五单,订单表全部成功,商品表 stock 字段变成 -3。后台库存显示「缺货」,前台却还能继续下单。碰到这种情况,我一般先确认改动有没有真的生效。

原因不复杂,扣库存的 UPDATE 写在下单事务提交之后,事务只包住了订单写入,两个请求同时读到 stock=2 都判断够卖。判断和扣减之间没有任何锁,读到的值早被别人改过。

「库存扣减超卖」这个问题文档写得含糊,实际行为以当前版本为准,别照旧版本的结论套。日志级别临时调高可以,记得改回去,长期开着既占磁盘又掩盖真问题。

UPDATE wp_sku SET stock = stock - 2
WHERE id = 9 AND stock >= 2;
-- affected_rows = 0 表示库存不足,回滚事务
SHOW WARNINGS;

具体做法是,把判断和扣减合成一条 SQL,写成 UPDATE 带 stock >= 数量 的条件,用受影响行数判断成功与否;受影响行数为 0 就直接回滚并返回库存不足,不要再查一次库存做二次判断;给 stock 字段加无符号约束,扣成负数时数据库直接报错,能兜住漏改的分支。

最后用这个标准收尾,两个终端同时跑同样的下单脚本,只有一个成功,stock 停在 0 而不是负数。

作者 9823189