MySQL 8.0原子DDL指单个DDL语句自身具备崩溃安全性,通过事务性数据字典(InnoDB系统表)和innodb_ddl_log实现元数据、存储引擎操作与binlog写入的原子绑定,宕机后自动回滚或重做,避免半成品状态。

MySQL 8.0 的原子 DDL 不是让 DDL 支持 BEGIN/COMMIT,而是让单个 DDL 语句自身具备崩溃安全(crash-safe)能力——服务器中途宕机,重启后不会留下“半改完”的表或残留临时文件。
为什么旧版 DDL 在崩溃时会出问题
MySQL 5.7 及之前版本的元数据分散在多个地方:.frm 文件、MyISAM 系统表(如 mysql.user)、InnoDB 内部字典。执行 ALTER TABLE 时,这些组件更新不同步:
- 先改 .frm 文件(文件系统操作)
- 再更新 InnoDB 数据字典(事务性操作)
- 最后重命名物理文件(
rename()系统调用)
任一环节崩溃(比如断电发生在第 2 步和第 3 步之间),就会出现元数据与文件不一致:InnoDB 认为列已加好,但 .frm 还是旧结构,查询时直接报 Table definition has changed, please retry transaction。DBA 得手动解析 .frm、重建表、校验数据——这不是修复,是抢救。
MySQL 8.0 怎么靠 Data Dictionary 实现原子性
核心变化是:所有元数据(表、列、索引定义等)统一存进 InnoDB 引擎管理的系统表里,比如 mysql.tables、mysql.columns、mysql.indexes。它们和你的业务表一样,走同一套 ACID 流程:
-
ALTER TABLE内部被拆成一组对这些系统表的INSERT/UPDATE操作 - 这些操作包裹在一个隐式事务中,写入
redo log并 fsync 到磁盘 - 同时,DDL 过程中关键步骤(如删文件、建临时表)的操作指令,会先记入隐藏表
mysql.innodb_ddl_log - 崩溃恢复时,InnoDB 根据
redo log回滚未提交的元数据变更;再根据innodb_ddl_log补全或清理物理动作(如真正删除.ibd文件)
所以你看到的是:杀掉 mysqld 进程后,ls data/mydb/ 找不到 #sql-* 临时文件,SHOW CREATE TABLE 仍显示原结构——不是没执行,是它根本没“生效”。
哪些 DDL 真正受原子性保护
不是所有语句都享受这套机制。只有明确由事务性数据字典参与管理的对象才支持:
- 支持:对
InnoDB表的CREATE/DROP/ALTER、TRUNCATE TABLE、CREATE OR REPLACE VIEW、CREATE PROCEDURE等 - 不支持:
INSTALL PLUGIN、CREATE SERVER、ANALYZE TABLE(它只是采样,不改元数据) - 注意:
DROP DATABASE是原子的,但物理文件删除被延迟到 post-DDL 阶段——这意味着崩溃后重启,data/mydb/目录可能还存在,但INFORMATION_SCHEMA.SCHEMATA已查不到该库,后续会自动清空
如果你用的是 MyISAM 或 Memory 表,原子 DDL 不生效——因为它们不依赖事务性数据字典。
最容易被忽略的“伪原子”陷阱
原子 DDL 保证的是单条语句的崩溃安全,但它不解决并发锁冲突。一个常见盲区是长事务阻塞 DDL:
- 一个运行了 15 分钟的
SELECT ... FOR UPDATE持有MDL_SHARED_WRITE锁 - 此时执行
ALTER TABLE t ADD COLUMN x INT,会被卡在 MDL 升级阶段,等待那个长事务结束 - 结果不是 DDL 失败,而是所有后续
INSERT/UPDATE全部排队等这个 DDL,雪崩式阻塞
这跟原子性无关,但线上事故里 70% 的 DDL 异常表现,其实都卡在这儿——DDL 自身很健壮,但它等不及你那条忘了加 LIMIT 的慢查询。











