原子ddl是备份一致性的隐性前提,它通过崩溃安全机制消除元数据撕裂,确保--single-transaction读取的字典状态始终逻辑干净、非此即彼,从而让一致性快照真正可信赖。

mysqldump --single-transaction 本身不依赖原子 DDL,但备份期间若发生 DDL,原子性会决定你能否安全恢复——关键不是“用了原子 DDL 就自动一致”,而是它防止了备份中途因崩溃导致的元数据撕裂,让 --single-transaction 的一致性快照真正可信赖。
为什么原子DDL是备份一致性的隐性前提?
MySQL 8.0 的 --single-transaction 备份靠的是 InnoDB 的一致性快照(REPEATABLE READ),但它只冻结数据行版本,不冻结表结构。如果备份过程中有人执行 ALTER TABLE,旧版本事务仍能看到原结构,新事务看到新结构——这本身是隔离性正常表现。但问题出在**崩溃场景**:
• 若没原子 DDL(如 5.7),ALTER TABLE 中途宕机,可能留下 .frm 文件旧、InnoDB 字典新、binlog 缺失的“三头怪”状态;
• 此时 --single-transaction 备份读到的可能是“半更新”的字典视图,恢复后 SHOW CREATE TABLE 和实际存储引擎结构对不上,后续 DML 或复制直接报错。
原子 DDL 消除了这种中间态:崩溃后回滚全部变更,备份进程始终面对一个逻辑干净的、非此即彼的元数据状态。
备份窗口内执行DDL的正确姿势
即使有原子 DDL,也不能在 --single-transaction 备份运行时随意执行 DDL。常见误操作和应对方式:
- 禁止并发执行
ALTER TABLE、DROP TABLE、TRUNCATE TABLE:这些会触发ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION错误,中断备份 - 若必须改结构,应先停写、完成 DDL、再启动备份;或改用物理备份(如
mysqlbackup)并配合--no-lock(仅限 InnoDB) -
DROP TABLE t1, t2这类批量语句仍需警惕:它虽在 8.0 中更安全(失败表不会影响已删表),但不提供跨表原子性——备份中若只删掉一个表就中断,备份文件里就缺一张表,恢复后业务直接报错
GTID 备份必须配 --set-gtid-purged=ON
原子 DDL 保证了单条语句的元数据一致性,但不解决 GTID 快照漂移问题。典型现象:
• mysqldump --all-databases --single-transaction 导出时,@@global.gtid_executed 是动态值,可能比一致性快照滞后数百个事务;
• 恢复后配置从库,Retrieved_Gtid_Set 和 Executed_Gtid_Set 对不上,复制跳过事务或卡死。
必须显式加参数:
mysqldump --single-transaction --master-data=2 --set-gtid-purged=ON ...
--set-gtid-purged=ON 会让 mysqldump 在 dump 文件头部注入 SET @@GLOBAL.GTID_PURGED = '...';,该值取自备份开始时刻的 gtid_executed,与快照严格对齐。注意:
• 不要设为 OFF(丢失 GTID 上下文);
• 不要设为 AUTO(8.0 默认行为,但实际取值时机不可控);
• 若备份前已清空 binlog,需确保 gtid_purged 值被正确记录,否则恢复实例无法加入复制拓扑。
非 InnoDB 表会让整个备份失去原子保障
原子 DDL 只对 ENGINE=InnoDB 表生效。如果你的库含 MyISAM、CSV 或 Memory 表(如 mysql.general_log),它们的 DDL 完全不走原子流程:
• ALTER TABLE mysql.general_log ENGINE=InnoDB 本身是原子的,但执行过程会锁全表,线上慎用;
• 备份时若这些表被并发修改,--single-transaction 完全不保护它们,导出内容可能前后不一致;
• 解决方案只有两个:
– 加 --lock-all-tables(阻塞所有写入,适合维护窗口);
– 或提前把非 InnoDB 表转成 InnoDB,并确认 SHOW CREATE TABLE 输出含 ENGINE=InnoDB。











