ddl语句在mysql中根本不在事务路径上,其执行绕过innodb事务管理器,直接走元数据锁+文件系统变更+强制刷盘,解析阶段即触发隐式提交,使此前dml落库、后续rollback失效,且set autocommit=0或begin完全无效。

DDL语句在MySQL中根本不在事务路径上
MySQL把 CREATE、ALTER TABLE、DROP VIEW 这类DDL语句设计为“非事务性操作”,它们的执行流程绕过InnoDB事务管理器,直接走元数据锁 + 文件系统变更 + 强制刷盘。哪怕存储过程开头写了 START TRANSACTION,碰到第一个DDL,MySQL解析器就立刻触发隐式提交——不是执行完再提交,而是在执行前就把当前事务清空。
SET AUTOCOMMIT=0 或 BEGIN 完全无效
你设了 SET autocommit = 0,写了 BEGIN,甚至用 DECLARE EXIT HANDLER 捕获异常,都没用。原因很直接:DDL不看事务开关。它触发的是服务层强制提交逻辑,和客户端是否开启事务无关。常见误判场景包括:
- 以为
ALTER TABLE ... ALGORITHM=INSTANT能进事务——实际仍隐式提交 - 用
PREPARE+EXECUTE动态拼接DDL——照样触发 - 在存储过程中嵌套调用含DDL的另一个存储过程——隐式提交层层传递
怎么确认事务已经被DDL悄悄切开?
别靠猜,用运行时状态验证:
- 执行DDL前查
SELECT @@in_transaction,值应为1 - 执行DDL后立刻再查,值变成
0(MySQL 8.0+) - 或者查
information_schema.INNODB_TRX,记录消失即说明事务已清空 - 在应用层(如PyMySQL),用
conn.in_transaction打点记录,比SQL更早暴露问题
真正能落地的规避方式只有解耦
DDL不可回滚是事实,接受它,然后组织代码:
- 把DDL单独抽成独立脚本或部署步骤,在服务停写或低峰期执行
- 业务DML必须放在新事务里,且与DDL物理隔离(不同连接、不同存储过程)
- 若必须动态判断(比如“字段不存在才加”),先查
INFORMATION_SCHEMA.COLUMNS,再拼接执行DDL,但绝不混在同一事务块内 - 对关键表结构变更,优先用
pt-online-schema-change,它用影子表模拟原子性,但本身也不进事务
隐式提交不报错、不警告,只安静地破坏事务预期——最危险的地方,就是你以为它还没发生。











