mysql 8.0原子ddl安全性源于元数据统一存入innodb,使元数据变更具备事务特性(undo log、crash recovery、行级锁),彻底避免.frm与.ibd不一致等崩溃后状态错乱问题。

MySQL 8.0 的原子 DDL 安全性不来自“事务里能执行 DDL”,而来自元数据表全部迁入 InnoDB
MySQL 8.0 的原子 DDL 更安全,核心原因是它把原来分散在文件系统(.frm)、MyISAM 系统表(mysql.user 等)和 InnoDB 内部字典(SYS_TABLES)里的元数据,全部统一存进 InnoDB 引擎的 mysql.* 系统表中。这意味着:元数据变更本身就成了标准的事务操作,能走 undo log、能被 crash recovery 回滚、能加行级 X 锁。
这直接堵死了 MySQL 5.7 及以前最典型的崩溃后不一致场景:
-
ALTER TABLE到一半宕机,.frm文件已更新但.ibd还是旧结构 -
DROP TABLE时删了.frm却没清掉ibdata1里的 InnoDB 字典记录 - 主库 binlog 记了 DDL,但从库因文件残留或缓存错位解析失败
现在所有元数据写入都经由 InnoDB 的事务路径,失败即回滚,无需人工干预。
哪些元数据表实际参与了原子 DDL 流程
真正被事务保护的,是 mysql 库下这批 InnoDB 表:
-
mysql.columns、mysql.tables、mysql.indexes:表结构、字段、索引定义 -
mysql.tablespaces、mysql.schemata:库、表空间元信息 -
mysql.procs、mysql.triggers、mysql.views:存储过程、触发器、视图定义
这些表不再只是“只读字典”,而是可被 DDL 操作实时修改的事务表。例如 ALTER TABLE t ADD COLUMN c INT 实际会:
- 向
mysql.columns插入一行新字段记录 - 更新
mysql.tables中该表的se_private_data字段(含列数、版本等) - 在
mysql.indexes中补充隐式主键索引(若无显式主键)
所有这些 DML 都打包在一个内部事务里,提交前还会写入 mysql.innodb_ddl_log(不可见系统表),用于物理层回滚。
为什么你查不到 innodb_ddl_log,但它又关键
mysql.innodb_ddl_log 是原子 DDL 的“物理操作日志”,记录诸如:
- 创建临时表文件的路径与 ID
- 重命名
#sql-xxx.ibd到正式名的操作步骤 - 删除旧
.ibd文件的清理指令
它不对外暴露,普通用户无法 SELECT 或 DROP,连 SHOW CREATE TABLE mysql.innodb_ddl_log 都会报错,除非开启 debug 模式并设置 SET SESSION debug='+d,skip_dd_table_access_check'。
但它真实存在,且 crash 时会被 InnoDB 自动读取并执行逆向操作。比如:
- 若
ALTER TABLE ... ALGORITHM=INPLACE失败,它会删掉已创建的索引 B-tree 页面 - 若
RENAME TABLE卡在最后一步,重启后它会把名字改回去,并清理残留锁
这个表的存在,说明原子性不是靠“跳过失败步骤”实现的,而是靠“记录每一步,失败就倒着干”。
容易被忽略的元数据一致性盲区
即使元数据表全部事务化,仍有三类场景不会自动兜底:
-
ALTER TABLE ... MODIFY COLUMN:类型变更常触发ALGORITHM=COPY,绕过原子 DDL 流程,退化为老式文件操作 - 跨库
RENAME TABLE db1.t1 TO db2.t2:涉及两个库的元数据事务,不支持原子性 - 客户端缓存了
information_schema.COLUMNS结果:查询结果可能滞后于实际mysql.columns状态,造成“看到没变”的错觉
执行前用 SELECT * FROM mysql.tables WHERE name = 't1' 查真实状态,比查 information_schema 更可靠。毕竟后者是只读快照,前者才是事务源。











