不能用触发器实现真正可靠的多级联动库存扣减,因其缺乏跨表事务协调能力、无法处理并发竞争,且链路变长时错误会层层掩盖;必须改用应用层显式事务配合select ... for update加唯一索引约束。

不能用触发器实现真正可靠的多级联动库存扣减——它缺乏跨表事务协调能力、无法处理并发竞争,且一旦链路变长(比如订单 → 子单 → 商品 → 仓库 → 仓位),错误会层层掩盖。
BEFORE INSERT 触发器里查多张表再扣减?别试
常见错误是想在 orders 表的 BEFORE INSERT 触发器里:先查 order_items,再查 products,再查 warehouses,最后更新 stock_locations。这看似“联动”,实际踩中三个硬伤:
- MySQL 不允许触发器内嵌套多层
SELECT ... FOR UPDATE,锁会被释放或失效 - 多个
SELECT之间存在时间窗口,其他事务可修改中间状态(如商品被另单锁定、仓位被清空) - 触发器无法回滚已执行的部分操作;比如查完
products没问题,但查warehouses时发现无货,此时products的行锁早已释放,别人立刻能抢走库存
AFTER INSERT + 多表 UPDATE 触发器会报 ERROR 1442
有人尝试在 orders 上建 AFTER INSERT 触发器,然后依次 UPDATE order_items、UPDATE products、UPDATE stock_locations。这是错的:
-
UPDATE order_items会直接触发order_items上的触发器(如果存在),形成隐式递归,MySQL 立即报ERROR 1442 - 即使你只更新
products和stock_locations,这两张表若也定义了触发器,同样可能连锁触发 - 更危险的是:
AFTER INSERT已错过拦截时机,订单记录已落库,用户看到“下单成功”,但后续任意一环失败(如仓位不足),库存就错乱且无感知
真要联动,必须放弃触发器,改用应用层显式事务
多级库存(比如按仓库+仓位粒度扣减)本质是分布式状态变更,触发器的单语句上下文根本撑不住。可行做法只有:
- 应用层开启事务,按顺序执行:
SELECT ... FOR UPDATE锁定products行 → 锁定对应warehouses行 → 锁定目标stock_locations行 - 每一步都检查可用量,任一环节不满足立即
ROLLBACK - 全部校验通过后,统一执行
UPDATE扣减,并记录扣减日志(含各层级 ID 和数量) - 加唯一索引约束(如
(product_id, warehouse_id, location_id))防止重复扣减
触发器只适合单点、确定、无分支的同步动作,比如「订单插入 → 商品表 stock 减 1」。一旦涉及路径选择(哪个仓?哪个位?)、条件跳转(缺货时是否启用调拨?)、或需外部 API 协同(调用 WMS 接口),就必须交还给应用代码控制。











