mysql 5.7及更早版本中,alter table在事务内会隐式提交当前事务,导致前面dml无法回滚;mysql 8.0虽支持原子ddl,但alter table等ddl语句仍强制中断事务,不改变隐式提交行为。

ALTER TABLE 在事务里会悄悄提交前面所有 DML
MySQL 5.7 及更早版本中,ALTER TABLE 一执行,当前事务立刻被隐式提交——不是报错,也不是等待,而是静默结束。你写 BEGIN → 插入几条数据 → ALTER TABLE → ROLLBACK,结果是插入的数据还在,ROLLBACK 像没发生一样。
这是因为 DDL 不受 autocommit=OFF 控制,也不吃事务边界。哪怕事务里只有 SELECT 和 ALTER TABLE,执行完 ALTER TABLE 后,事务状态就变成“已提交”,后续语句自动进入新事务。
-
SHOW VARIABLES LIKE 'autocommit'设为OFF对 DDL 完全无效 - 事务被“劈开”:DDL 前的 DML 可回滚,DDL 后的 DML 属于新事务,不受之前
ROLLBACK影响 - 存储过程中用
START TRANSACTION包裹 DDL + DML,同样失效——开发常踩这个坑
MySQL 8.0 的 INSTANT 算法没解决隐式提交问题
MySQL 8.0 引入了 INSTANT 算法(如 ADD COLUMN),但它只优化执行速度和锁粒度,**不改变 DDL 的事务语义**。只要语句属于 DDL 类别(CREATE、DROP、ALTER、RENAME、TRUNCATE),仍会触发隐式提交。
DROP DATABASE、CREATE TABLE 这类操作在 8.0 中依然强制提交;哪怕用了 ALGORITHM=INSTANT,也无法放进事务回滚点里。
-
ALTER TABLE t ADD COLUMN c INT ALGORITHM=INSTANT仍导致事务中断 - 仅少数 DDL(如加列、改列注释)支持
INSTANT,但绝大多数(索引增删、字段类型变更、分区操作)仍走INPLACE或COPY,全部隐式提交 - 别被“Online”误导:
ONLINE DDL解决的是锁表问题,不是事务原子性问题
DDL 和 DML 混用时的一致性风险真实存在
典型场景是配置驱动型更新:先在 config 表里写个开关,再对主表做结构变更,最后统一生效。如果把这两步塞进一个事务,ALTER TABLE 一执行,config 表的变更就提前落库了,后续若失败,无法靠 ROLLBACK 撤回,系统状态直接撕裂。
更隐蔽的问题是监控或校验逻辑失效:比如你在事务开头记下某张表的行数,中间执行 ALTER TABLE,结尾再查一次——两次查询其实跨了两个事务,快照可能不一致,校验结果不可信。
- DDL 前后执行的
SELECT可能读到不同一致性视图(尤其在REPEATABLE READ隔离级别下) - 基于事务 ID 或 GTID 做幂等判断的逻辑,在 DDL 插入后会断层
- binlog 中 DDL 和 DML 分属不同事务事件,下游解析时顺序和依赖关系容易错乱
安全做法是物理隔离 DDL 和 DML
真正可行的方案不是“怎么让 DDL 支持回滚”,而是承认它天生不事务安全,从流程上规避混合执行。上线脚本、迁移工具、自动化平台都应把 DDL 单独拎出来,与 DML 分开执行、分开验证、分开重试。
如果必须保证“结构+数据”原子生效,得换思路:比如用影子表(t_new)先建好结构、导入数据、校验完成,再用 RENAME TABLE 原子切换——注意 RENAME TABLE 本身也是 DDL,所以切换动作要单独成步,不能裹在业务事务里。
- 运维脚本中用
mysql -e "ALTER TABLE ..."单独调用,而非拼进业务 SQL 字符串里 - 应用层不要在
@Transactional方法里调用 DDL 相关 DAO 方法 - DBA 审核 SQL 时,重点检查是否出现
BEGIN/START TRANSACTION后紧跟CREATE、ALTER、DROP
最危险的不是报错,而是看起来成功了——DDL 静默提交,事务边界消失,而你还在按旧假设写后续逻辑。











