before update 不适合库存预警,因其本质是操作拦截而非状态判定;它虽可读取old/new值,但更新尚未生效,无法可靠判断更新后是否真正低于阈值,且可能误阻断合法更新。

不能在 BEFORE UPDATE 中可靠实现库存不足预警——它会阻断合法更新,且无法感知“更新后是否真低于阈值”。
为什么 BEFORE UPDATE 不适合库存预警
库存预警的本质是「状态判定」,不是「操作拦截」。而 BEFORE UPDATE 的定位是修改数据前的干预:它能读取 OLD 和 NEW 值,但此时记录尚未写入表,任何基于当前行状态的判断(比如“更新后是否 current_stock ”)都可能因并发或事务未提交而失真;更关键的是,一旦你在里面执行 <code>INSERT INTO alerts 或调用存储过程发通知,就等于把业务侧的异步动作强耦合进原子更新流程,极易导致锁等待、死锁或事务回滚失败。
- 触发器里发邮件/SMS?MySQL 默认不支持,得靠外部脚本轮询或事件调度器补位
- 想用
SIGNAL中断更新并报错?那用户一次正常减库存操作就会被拦住,不符合“预警”本意(预警 ≠ 拒绝) -
BEFORE阶段无法保证你看到的min_threshold是最新值——如果该字段本身也在同一条UPDATE里被改了,NEW.min_threshold才有效,但逻辑已混乱
AFTER UPDATE 才是正确入口点
库存变化完成、事务即将提交时,状态才真实稳定。AFTER UPDATE 触发器能安全读取最终值,并决定是否记录预警。注意必须加条件过滤,避免无意义触发:
DELIMITER ;;
CREATE TRIGGER inventory_low_stock_alert
AFTER UPDATE ON product_stock
FOR EACH ROW
BEGIN
IF NEW.current_stock = NEW.min_threshold THEN
INSERT INTO alerts (product_id, message, alert_type, created_at)
VALUES (
NEW.id,
CONCAT('库存跌至', NEW.current_stock, ',低于阈值', NEW.min_threshold),
'low_stock',
NOW()
);
END IF;
END;;
DELIMITER ;
- 只在“刚跌破阈值”那一刻触发(
OLD >= threshold且NEW ),防抖 - 插入
alerts表而非直接调用外部服务,解耦后续通知逻辑 - 依赖
product_stock.min_threshold字段——它必须存在且非 NULL,否则NEW.min_threshold为 NULL 会导致整个条件失效
真正要警惕的坑:并发更新下阈值误判
两个事务同时对同一商品减库存,都从 15 减到 5(阈值设为 10),按理说只有第一个事务该触发预警。但 AFTER UPDATE 无法感知“谁先提交”,可能导致重复预警。解决方案不是加锁,而是用「唯一约束 + 忽略冲突」:
- 给
alerts表加联合唯一索引:UNIQUE KEY uk_product_day (product_id, DATE(created_at)) - 把
INSERT改成INSERT IGNORE,同一天同一商品只留一条预警 - 或者用
INSERT ... ON DUPLICATE KEY UPDATE更新updated_at时间戳,便于前端去重
预警逻辑越靠近数据层越稳定,但别让它承担不该背的职责——发通知、调 API、写日志这些,交给应用层定时扫 alerts 表更可控。触发器只做一件事:在数据落库的瞬间,记一笔“这里可能需要人看一眼”。











