会,触发器与主语句共享同一事务上下文,报错时整个事务(含主语句和触发器内所有dml)自动回滚;mysql不支持触发器内commit/rollback或自治事务,before/after触发器错误均导致原子性回滚。

触发器和主语句共享同一个事务上下文
MySQL 触发器不是独立事务,它直接运行在主语句(INSERT、UPDATE、DELETE)所属的事务里。只要触发器里执行出错——比如 SIGNAL SQLSTATE '45000'、字段不存在、违反外键或唯一约束——整个事务就会被标记为失败,主语句和触发器内已执行的所有 DML 全部撤销。
这不是“选择性回滚”,而是引擎强制保障的原子性:InnoDB 会把触发器逻辑当作主语句的一部分来对待。哪怕你只在触发器里写了 INSERT INTO log_table,而 log_table 缺少一个非空字段,INSERT 主语句也会失败,且原表数据不会变更。
BEFORE 和 AFTER 触发器对回滚的影响一致但时机不同
BEFORE 触发器能修改 NEW 值并提前终止语句;AFTER 触发器无法改数据,但它的错误照样导致回滚。很多人误以为 AFTER 是“事后补救”,其实它只是执行得晚一点,仍处于同一事务生命周期中。
-
BEFORE INSERT中SET NEW.created_at = NOW()若引用了不存在的列,整条INSERT失败 -
AFTER INSERT中往审计表写日志时字段不匹配,主记录和日志都消失 - 两者都无法绕过事务边界,也都不允许在体内写
COMMIT或ROLLBACK(否则报ERROR 1305)
autocommit=1 时看似没回滚,其实是单语句事务
默认连接下 SELECT @@autocommit 返回 1,意味着每条语句都是独立事务。这时触发器报错,INSERT 确实会“回滚”,但因为没其他语句可一起撤回,你只看到主语句失败,误以为“没回滚”。实际是:事务启动 → 执行主语句 → 触发器报错 → 整个单语句事务回滚 → 连接自动提交下一个语句。
验证方式很简单:
START TRANSACTION; INSERT INTO t (x) VALUES (1); -- 假设触发器失败 SELECT * FROM t WHERE x = 1; -- 返回空 ROLLBACK;
如果这步查不到数据,说明回滚确实发生了;如果没加 START TRANSACTION,就永远看不到多语句间的回滚效果。
常见静默失败场景必须手动检查
触发器报错经常不直接抛给客户端,尤其语法类错误(如拼错 NEW.col_name)或跨库路径缺失(只写 INSERT INTO audit_log 没带库名)。此时主语句失败,但应用可能只收到模糊提示甚至无报错。
立刻执行以下两步定位真实原因:
- 执行完疑似失败的 DML 后,马上跑
SHOW WARNINGS—— 很可能看到Warning 1327 Undeclared variable: NEW.invalid_col - 确认错误日志是否启用:
SELECT @@log_error,并检查log_error_verbosity是否为3(MySQL 8.0+),否则触发器警告会被忽略
真正容易被忽略的是:触发器没有“局部成功”概念。它要么全执行完,要么整个事务归零——连主语句的自增 ID 都会回退(LAST_INSERT_ID() 在回滚后失效),这点在调试时特别容易误判。











