mysql在事务中执行ddl会隐式提交当前事务,即解析ddl语句时立即强制提交此前所有未提交的dml操作,导致rollback失效;该行为不受set autocommit=0或begin控制,且truncate、grant等语句同样触发隐式提交。

DDL语句执行前就已隐式提交
事务中只要出现 ALTER TABLE、DROP TABLE、CREATE INDEX 等 DDL 语句,MySQL 会在真正解析它之前,先把当前事务里所有未提交的 DML(如 INSERT、UPDATE)强制 COMMIT。这意味着:ROLLBACK 执行时,前面的修改早已落盘,根本无“滚”可言。
-
TRUNCATE TABLE也属于 DDL,同样触发隐式提交,别被“清空数据”的表象迷惑 - 哪怕你写了
SET autocommit = 0或显式BEGIN,也拦不住 DDL 的隐式提交行为 - 错误发生在 DDL 阶段(比如字段不存在),不代表前面 DML 还在事务里——它们已在解析 DDL 前就被提交了
事务根本没真正启动或已被中断
回滚失败,常常不是回滚错了,而是压根没进事务。最常见的是 AUTOCOMMIT=1(MySQL 默认值),此时每条 SQL 都是独立事务,ROLLBACK 对它无效。
- 用
SELECT @@autocommit;确认当前会话状态;应为0才能手动控制事务 - MyISAM 引擎表不支持事务,
START TRANSACTION和ROLLBACK都被静默忽略,且不报错 - 存储过程中未声明
DECLARE EXIT HANDLER,异常后连接可能断开或复用,导致事务悬而未决 - 应用层(如 Python + PyMySQL)必须显式
conn.rollback(),驱动不会自动捕获异常并回滚
事务边界被意外打破
看似在一个事务块里,实际可能因某些操作提前终结了事务生命周期。
-
GRANT、REVOKE、CREATE USER等 DCL 语句也会隐式提交当前事务 - 执行
LOCK TABLES会隐式关闭当前事务,必须先COMMIT或ROLLBACK再加锁 - 同一连接中连续执行两次
BEGIN,第一个事务会被隐式提交 - 事务未提交也未回滚,连接异常断开时,MySQL 会自动回滚——但这是服务端行为,不可控且不保证原子性
Spring/Hibernate 中事务失效的典型陷阱
@Transactional 注解生效,依赖于代理机制和异常传播路径,稍有偏差就失效。
- 自己
try/catch了异常但没重新抛出,Spring 完全感知不到失败,自然不触发回滚 - 调用的是本类内无代理的方法(self-invocation),事务切面未生效
-
rollbackFor指定的异常类型与实际抛出不符(比如配置只回滚RuntimeException,但实际抛的是SQLException) - 表引擎仍是 MyISAM,即使注解生效、代码逻辑正确,底层也无法回滚











