会,而且是彻底回滚——触发器失败不是“局部报错”,而是直接让整个事务作废,包括原sql和触发器里已经执行的语句;触发器无独立事务上下文,其内部错误(如signal、约束冲突、非法赋值)会导致从begin开始的所有变更全部回滚,且禁止在其中执行commit/rollback。

会,而且是彻底回滚——触发器失败不是“局部报错”,而是直接让整个事务作废,包括原SQL和触发器里已经执行的语句。
触发器失败 = 主语句失败 = 整个事务回滚
MySQL 触发器没有独立事务上下文,它只是主事务中一段自动附加的逻辑。一旦触发器内抛出错误(比如 SIGNAL SQLSTATE '45000'、字段赋值非法、违反外键或唯一约束、调用不存在的函数),整个事务立刻中止,所有变更全部回滚。
- 哪怕你只在
BEFORE INSERT里做了三行SET NEW.x = ...,最后一行写了个SELECT 1/0,整条INSERT都不会落库 -
AFTER UPDATE中去更新另一张表失败,本表那行刚写入的数据也会被撤回 - 回滚起点是事务开始处(
BEGIN或隐式事务起点),不是从触发器开始算
为什么不能在触发器里写 COMMIT / ROLLBACK
MySQL 明确禁止在触发器中执行 COMMIT、ROLLBACK 或 START TRANSACTION,否则直接报错 ERROR 1305 (42000): SAVEPOINT does not exist 或类似语法错误。
- 这不是权限问题,是设计限制:触发器必须“寄生”在外部事务里,强行加事务控制会破坏原子性
- 想实现“主表失败但日志要留下”?这种需求本身违背 MySQL 的事务模型,硬塞进触发器只会导致不可控状态
- 真有审计日志必须落盘,应该用应用层异步写入,或通过
INSERT ... ON DUPLICATE KEY UPDATE+ 唯一键兜底
BEFORE 和 AFTER 触发器对事务的影响差异
两者都能导致主事务回滚,但作用时机和能力完全不同,容易混淆。
-
BEFORE可修改NEW值,并能在语句执行前就终止它(比如校验不通过就SIGNAL) -
AFTER无法修改当前行数据,但它执行时原语句已写入本表——所以它的失败会导致本表刚写的那行也被回滚 - 常见误操作:
AFTER UPDATE中又去UPDATE同一张表 → 触发递归或死锁,MySQL 8.0 默认禁止,5.7 可能卡住 - 真正安全的
AFTER操作只限于只读旁路动作,比如发消息、写外部日志表(且必须带库名前缀,如sys_log.audit_log)
排查触发器失败最常被忽略的点
触发器失败往往静默发生,客户端看不到真实错误,连 SHOW WARNINGS 都可能漏掉,除非你主动查。
- 执行疑似失败的
INSERT/UPDATE后,**立刻执行SHOW WARNINGS**,里面常有真实报错,比如Unknown column 'xxx' in 'field list' - 确认
log_error_verbosity = 3,否则触发器内部警告会被忽略;同时检查log_error文件路径是否可写 - 跨库操作必须写全路径:
INSERT INTO sys_log.audit_log,不能只写audit_log - 注意
sql_mode:如果含STRICT_TRANS_TABLES,而UPDATE实际没改任何字段(如SET name = name),整个语句和触发器都会被跳过
真正难处理的不是“怎么写触发器”,而是“什么时候不该用触发器”。一旦业务逻辑需要失败降级、局部提交或跨表写入,就该把这部分移到应用层或存储过程里——触发器只适合简单、确定、无副作用的原子操作。











