mysql 8.0原子ddl仅对特定操作(如add column、drop index等)提供事务级原子性,不支持modify column等操作;其依赖innodb系统表、ddl_log表和xa事务协同实现物理文件、字典与binlog三同步。

原子DDL不是“所有ALTER都回滚”,而是“只对特定操作启用事务包装”
MySQL 8.0 的原子 DDL 不是给每个 ALTER TABLE 自动加个 BEGIN/COMMIT 就完事。它只对官方明确标注为 Atomic DDL supported: Yes 的子集生效,比如 ADD COLUMN(末尾添加)、DROP INDEX、RENAME TABLE(同库内)、ALTER TABLE ENGINE=InnoDB。而 MODIFY COLUMN、CHANGE COLUMN、OPTIMIZE TABLE 这些操作压根不进原子事务流程——它们失败时可能已改了部分元数据,但无法整体回滚。
关键点在于:原子性 ≠ 语法合法,也不等于“用了 MySQL 8.0 就安全”。你执行一条 ALTER TABLE t1 MODIFY c1 INT,哪怕服务器崩溃,重启后表结构可能处于中间态(比如列类型没改完但索引被删了),这不是 bug,是设计如此。
底层靠 InnoDB 系统表 + ddl_log 表协同完成“物理文件+字典+日志”三同步
原子性的实现依赖三个硬绑定层:
-
mysql.tables、mysql.columns等系统表全用 InnoDB 引擎存储,DDL 变成对这些表的普通 DML 操作,天然支持 ACID -
mysql.innodb_ddl_log是隐藏日志表,记录文件级操作(如重命名.ibd、删除临时文件)的前滚/回滚指令,确保崩溃后能补全或清理物理动作 - binlog 写入与字典更新在同一个 XA 事务中提交,避免主从之间出现“从库有新结构但主库没生效”的错位
所以崩溃恢复时,InnoDB 先按 undo log 回滚字典变更,再查 innodb_ddl_log 执行补偿动作(比如删掉本该 rename 却没完成的文件),最后刷新 buffer pool 中的 dict_table_t 对象。整个链条缺一不可。
ALGORITHM=INSTANT 和原子性完全无关,别混淆“快”和“安全”
ALGORITHM=INSTANT 只影响执行速度,它跳过拷表、不锁表、不生成临时文件,但它的成功与否不改变原子性机制是否触发:
- 支持 INSTANT 的操作(如
ADD COLUMN末尾加、SET DEFAULT)如果同时满足原子 DDL 条件,就既快又可回滚 - 不支持 INSTANT 的操作(如
DROP COLUMN)只要属于原子 DDL 支持列表,依然走完整事务流程,只是慢一点 - 混用风险:在显式事务里写
START TRANSACTION; ALTER TABLE ... ALGORITHM=INSTANT; ALTER TABLE ... MODIFY COLUMN;,第二句直接报错退出,且第一句已提交的变更不会回滚
验证原子性是否真生效,不能只看错误信息,得交叉查字典和物理状态
服务器异常重启后,光看 SHOW CREATE TABLE 不够。必须做两件事确认状态一致:
- 执行
SELECT * FROM INFORMATION_SCHEMA.INNODB_SYS_TABLES WHERE NAME = 'db/t1',确认SPACE和FLAG字段有值且未变 - 执行
SELECT FILE_NAME, ENGINE FROM INFORMATION_SCHEMA.FILES WHERE TABLE_NAME = 't1',核对返回的.ibd文件路径和引擎是否匹配INNODB_SYS_TABLES中记录 - 若两者脱节(比如字典显示表存在,但
FILES查不到对应.ibd),说明原子 DDL 在准备阶段就被拒绝了,典型错误是ERROR 1033或ERROR 1286,这时要检查表空间一致性,而不是重试 DDL
真正容易被忽略的是:原子性只管“改完或不改”,不管“能不能改”。MDL 锁冲突、磁盘满、外键约束校验失败这些都会让 DDL 直接失败,但它们不属于原子性失效,而是前置校验环节拦下的正常行为。











