MySQL 8.0 开始支持原子数据定义语言 (DDL) 语句. 此功能称为原子 DDL. 原子 DDL 语句将与 DDL 操作关联的数据字典更新, 存储引擎操作和二进制日志写入组合到单个原子事务中. 即使服务器在操作期间暂停, 也会提交事务, 并将适用的更改保留到数据字典, 存储引擎和二进制日志, 或者回滚事务.
通过在 MySQL 8.0 中引入 MySQL 数据字典, 可以实现 Atomic DDL. 在早期的 MySQL 版本中, 元数据存储在元数据文件, 非事务性表和存储引擎特定的字典中, 这需要中间提交. MySQL 数据字典提供的集中式事务元数据存储消除了这一障碍, 使得将 DDL 语句操作重组为原子事务成为可能.
官方文档:
https://dev.mysql.com/doc/refman/8.0/en/atomic-ddl.html
1, 支持的 DDL 语句
原子 DDL 功能支持表和非表 DDL 语句. 与表相关的 DDL 操作需要存储引擎支持, 而非表 DDL 操作则不需要. 目前, 只有 InnoDB 存储引擎支持原子 DDL.
: 受支持的表 DDL 语句包括 CREATE,ALTER 和 DROP 对数据库, 表, 表和索引, 以及语句 TRUNCATE TABLE 声明.
: 支持的非表 DDL 语句包括:
CREATE 和 DROP 语句, 以及 (如果适用)ALTER 存储程序, 触发器, 视图和用户定义函数(UDF) 的语句.
账户管理语句: CREATE,ALTER, DROP,, 如果适用, RENAME 报表用户和角色, 以及 GRANT 和 REVOKE 报表.
1.1, 原子 DDL 功能不支持以下语句:
: 涉及除存储引擎之外的存储引擎的与表相关的 DDL 语句 InnoDB.
:INSTALL PLUGIN 和 UNINSTALL PLUGIN 陈述.
:INSTALL COMPONENT 和 UNINSTALL COMPONENT 陈述.
:CREATE SERVER, ALTER SERVER 和 DROP SERVER 语句.
2, 原子 DDL 特性:
: 元数据更新, 二进制日志写入和存储引擎操作 (如果适用) 将合并为单个事务.
: 在 DDL 操作期间, SQL 层没有中间提交.
: 在适用的情况下:
数据字典, 程序, 事件和 UDF 高速缓存的状态与 DDL 操作的状态一致, 这意味着更新高速缓存以反映 DDL 操作是成功完成还是回滚.
DDL 操作中涉及的存储引擎方法不执行中间提交, 并且存储引擎将自身注册为 DDL 事务的一部分.
存储引擎支持 DDL 操作的重做和回滚, 这在 DDL 操作的 Post-DDL 阶段执行.
:DDL 操作的可见行为是原子的, 这会更改某些 DDL 语句的行为
注意:
原子或其他 DDL 语句隐式结束当前会话中处于活动状态的任何事务, 就好像您 COMMIT 在执行语句之前完成了一样. 这意味着 DDL 语句不能在另一个事务中, 在事务控制语句中执行 START TRANSACTION ... COMMIT, 或者与同一事务中的其他语句结合使用.
3,DDL 语句行为的变化
3.1,DROP TABLE:
如果所有命名表都使用原子 DDL 支持的存储引擎, 则操作是完全原子的. 该语句要么成功删除所有表, 要么回滚.
DROP TABLE 如果命名表不存在, 并且未进行任何更改(无论存储引擎如何), 则会失败并显示错误. 如下所示:
- mysql> CREATE TABLE t1 (c1 INT);
- mysql> DROP TABLE t1, t2;
- ERROR 1051 (42S02): Unknown table 'test.t2'
- mysql> SHOW TABLES;
- +----------------+
- | Tables_in_test |
- +----------------+
- | t1 |
- +----------------+
在引入原子 DDL 之前, DROP TABLE 虽然会报错误表不存在, 但是存在的表会被执行成功, 如下:
- mysql> CREATE TABLE t1 (c1 INT);
- mysql> DROP TABLE t1, t2;
- ERROR 1051 (42S02): Unknown table 'test.t2'
- mysql> SHOW TABLES;
- Empty set (0.00 sec)
注意:
由于行为的这种变化, DROP TABLE 会在 MySQL 5.7 主服务器上的部分完成 语句在 MySQL 8.0 从服务器上复制时失败. 要避免此故障情形, 请在 DROP TABLE 语句中使用 IF EXISTS 语法以防止对不存在的表发生错误
3.2,DROP DATABASE:
如果所有表都使用原子 DDL 支持的存储引擎, 则为 atomic. 该语句要么成功删除所有对象, 要么回滚. 但是, 从文件系统中删除数据库目录是最后一次, 并且不是原子事务的一部分. 如果由于文件系统错误或服务器暂停而导致数据库目录的删除失败, DROP DATABASE 则不会回滚事务.
3.3, 对于不使用原子 DDL 支持的存储引擎的表, 表删除发生在原子 DROP TABLE 或 DROP DATABASE 事务之外. 这样的表删除被单独写入二进制日志, 这在中断 DROP TABLE 或 DROP DATABASE 操作的情况下将存储引擎, 数据字典和二进制日志之间的差异限制为最多一个表 . 对于删除多个表的操作, 不使用原子 DDL 支持的存储引擎的表将在执行之前删除.
3.4,CREATE TABLE, ALTER TABLE, RENAME TABLE, TRUNCATE TABLE, CREATE TABLESPACE, 和 DROP TABLESPACE 对使用原子 DDL 支持的存储引擎表执行的操作要么完全提交或如果服务器的操作时停止回滚. 在早期的 MySQL 版本中, 这些操作的中断可能会导致存储引擎, 数据字典和二进制日志之间的差异, 或留下孤立文件. RENAME TABLE 如果所有命名表都使用原子 DDL 支持的存储引擎, 则操作只是原子操作.
3.5,DROP VIEW:
如果命名视图不存在且未进行任何更改, 则会失败. 在此示例中演示了行为更改, 其中 DROP VIEW 语句失败, 因为命名视图不存在, 如下:
- mysql> CREATE VIEW test.viewA AS SELECT * FROM t;
- mysql> DROP VIEW test.viewA, test.viewB;
- ERROR 1051 (42S02): Unknown table 'test.viewB'
- mysql> SHOW FULL TABLES IN test WHERE TABLE_TYPE LIKE 'VIEW';
- +----------------+------------+
- | Tables_in_test | Table_type |
- +----------------+------------+
- | viewA | VIEW |
- +----------------+------------+
在引入原子 DDL 之前, 使用 DROP VIEW 删除视图会报错, 但是存在的视图会被成功删除:
- mysql> CREATE VIEW test.viewA AS SELECT * FROM t;
- mysql> DROP VIEW test.viewA, test.viewB;
- ERROR 1051 (42S02): Unknown table 'test.viewB'
- mysql> SHOW FULL TABLES IN test WHERE TABLE_TYPE LIKE 'VIEW';
- Empty set (0.00 sec)
注意:
由于行为的这种变化, DROP VIEW 在 MySQL 5.7 主服务器上的部分完成 操作在 MySQL 8.0 从服务器上复制时会失败. 要避免此故障情形, 请在 DROP VIEW 语句中使用 IF EXISTS 语法以防止对不存在的视图发生错误.
3.6, 不再允许部分执行帐户管理声明. 帐户管理语句对所有命名用户成功或回滚, 如果发生错误则无效. 在早期的 MySQL 版本中, 为多个用户命名的帐户管理语句可能对某些用户成功, 而对其他用户则失败.
如下: 其中第二个 CREATE USER 语句返回错误但失败, 因为它无法对所有命名用户成功.
- mysql> CREATE USER userA;
- mysql> CREATE USER userA, userB;
- ERROR 1396 (HY000): Operation CREATE USER failed for 'userA'@'%'
- mysql> SELECT User FROM mysql.user WHERE User LIKE 'user%';
- +-------+
- | User |
- +-------+
- | userA |
- +-------+
在引入原子 DDL 之前, 第二个 使用 CREATE USER 语句创建用户会返回一个错误, 但是不存在的用户会成功创建,:
- mysql> CREATE USER userA;
- mysql> CREATE USER userA, userB;
- ERROR 1396 (HY000): Operation CREATE USER failed for 'userA'@'%'
- mysql> SELECT User FROM mysql.user WHERE User LIKE 'user%';
- +-------+
- | User |
- +-------+
- | userA |
- | userB |
- +-------+
注意:
由于行为的这种变化, MySQL 5.7 主服务器上部分会成功执行, 会在 MySQL 8.0 从服务器上复制时失败. 要避免此故障情形, 请在创建用户的命令中使用 IF EXISTS 或 IF NOT EXISTS 语法, 以防止与命名用户相关的错误.
4, 存储引擎支持: 目前只有 innodb 存储引擎支持原子 DDL
目前, 只有 InnoDB 存储引擎支持原子 DDL. 不支持原子 DDL 的存储引擎免于 DDL 原子性. 涉及豁免存储引擎的 DDL 操作仍然能够引入操作中断或仅部分完成时可能发生的不一致.
要支持重做和回滚 DDL 操作, InnoDB 请将 DDL 日志写入 mysql.innodb_ddl_log 表, 该表是驻留在 mysql.ibd 数据字典表空间中的隐藏数据字典表 .
要 mysql.innodb_ddl_log 在 DDL 操作期间查看写入表的 DDL 日志 , 请启用 innodb_print_ddl_logs 配置选项.
注意:
mysql.innodb_ddl_log 无论 innodb_flush_log_at_trx_commit 设置多少, 对表的 更改的重做日志 都会立即刷新到磁盘 . 立即刷新重做日志可以避免 DDL 操作修改数据文件的情况, 但是 mysql.innodb_ddl_log 由这些操作产生的对表的更改的重做日志 不会持久保存到磁盘. 这种情况可能会在回滚或恢复期间导致错误.
InnoDB 存储引擎分阶段执行 DDL 操作. DDL 操作 ALTER TABLE 可以在 Commit 阶段之前多次执行 Prepare 和 Perform 阶段:
准备: 创建所需对象并将 DDL 日志写入 mysql.innodb_ddl_log 表中. DDL 日志定义了如何前滚和回滚 DDL 操作.
执行: 执行 DDL 操作. 例如, 为 CREATE TABLE 操作执行创建例程.
提交: 更新数据字典并提交数据字典事务.
Post-DDL: 重播并从 mysql.innodb_ddl_log 表中删除 DDL 日志. 为了确保可以安全地执行回滚而不引入不一致性, 在最后阶段执行文件操作, 例如重命名或删除数据文件. 这一阶段还从删除的动态元数据 mysql.innodb_dynamic_metadata 的数据字典表 DROP TABLE,TRUNCATE TABLE 和该重建表其他 DDL 操作.
注意:
无论事务是提交还是回滚, DDL 日志都会在 Post-DDL 阶段重播并从表中删除 .mysql.innodb_ddl_log 如果服务器在 DDL 操作期间暂停, 则 DDL 日志应仅保留在表中. 在这种情况下, DDL 日志将在恢复后重播并删除.
在恢复情况下, 可以在重新启动服务器时提交或回滚 DDL 事务. 如果在重做日志和二进制日志中存在在 DDL 操作的提交阶段期间执行的数据字典事务, 则 该操作被视为成功并且前滚. 否则, 在 InnoDB 重放数据字典重做日志时回滚不完整的数据字典事务 , 并回滚 DDL 事务.
5, 查看 DDL 日志:
InnoDB 将 DDL 日志写入 mysql.innodb_ddl_log 表以支持重做和回滚 DDL 操作. 该 mysql.innodb_ddl_log 表是隐藏在 mysql.ibd 数据字典表空间中的隐藏数据字典表 . 与其他隐藏数据字典表一样, mysql.innodb_ddl_log 在非调试版本的 MySQL 中无法直接访问该 表.
来源: http://blog.51cto.com/fengfeng688/2146223