触发器失败时事务是否回滚取决于引擎类型、错误类型、autocommit模式及事务上下文:innodb下before触发器中signal会回滚事务,但myisam或autocommit=1时无效;ddl会隐式提交;触发器内禁止修改自身表、返回结果集或开启事务;需用continue handler捕获异常而非中断事务。

触发器失败时事务是否回滚,取决于引擎和错误类型
MySQL 的 BEFORE 触发器里执行 SIGNAL SQLSTATE '45000',会直接让主语句失败并触发整个事务回滚——这是 InnoDB 引擎保障的原子性行为。但前提是:表必须是 ENGINE=InnoDB,且当前连接未开启 autocommit=1。如果表是 MyISAM,或连接处于自动提交模式,ROLLBACK 根本无效,看起来就像“回滚失败”。
常见误判点:
-
SHOW CREATE TABLE t必须确认输出含ENGINE=InnoDB,不能只看建表语句有没有写 -
SELECT @@autocommit返回1就说明每条语句已自动提交,BEGIN后的修改无法靠ROLLBACK撤销 - DDL 语句(如
ALTER TABLE)在事务中执行会隐式提交,后续ROLLBACK只能回滚它之后的操作
触发器静默失败?立刻查 SHOW WARNINGS 和错误日志
触发器内语法错、列不存在、跨库表名没带库前缀,往往不抛给客户端,而是让主语句失败,但错误信息藏得深。执行完疑似失败的 INSERT 或 UPDATE 后,**马上执行 SHOW WARNINGS**,常能看到真实原因,比如:
Warning 1327 Undeclared variable: NEW.invalid_col
同时确认错误日志是否启用:
- 查配置:
SELECT @@log_error,路径通常是/var/log/mysql/error.log或/var/lib/mysql/hostname.err - 确保
log_error_verbosity = 3(MySQL 8.0+),否则触发器内的警告会被忽略 - 注意
sql_mode是否含STRICT_TRANS_TABLES:不含的话,空值插入可能被截断而非报错,触发器逻辑实际没跑
触发器里不能做的事,一碰就崩
MySQL 对触发器运行有硬性限制,违反即报错(如 ERROR 1442),且错误常不直观。最典型的是:
- 在触发器中对**同一张表**做
INSERT/UPDATE/DELETE→ 报ERROR 1442 (HY000);解决办法是改用临时表、或把逻辑移到应用层 - 触发器内调用的存储函数里用了
SELECT ... FOR UPDATE或START TRANSACTION→ MySQL 禁止在触发器上下文中开启事务 - 写了
SELECT col FROM t却没接INTO @var→ 报ERROR 1415 (0A000):“Not allowed to return a result set” - 跨库操作没写全路径,例如想写日志到
sys_log.audit_log,却只写INSERT INTO audit_log→ 默认去当前库找,找不到就静默失败
想记录错误又不让事务失败?用 DECLARE CONTINUE HANDLER
如果目标不是中断主语句,而是捕获异常后写日志、发告警、或降级处理,就不能靠 SIGNAL。必须用 DECLARE CONTINUE HANDLER FOR SQLEXCEPTION 捕获,并确保 handler 类型是 CONTINUE(不是 EXIT),否则 handler 执行完主语句仍可能继续,造成数据不一致。
示例片段:
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN INSERT INTO trigger_error_log VALUES (NOW(), 'orders insert', 'failed'); END;
关键点:
- handler 内不能调用会引发新异常的语句(如往同一张表插日志)
- 避免在 handler 里再
SIGNAL,否则又回到“中断事务”路径 - 真正复杂的校验、外部服务调用、多表更新,应该从触发器里移出,放到应用层或存储过程中统一控制事务边界
EXIT 导致后续逻辑跳过。











