文档首页
MySQL 8.4 参考手册
相关文档 下载本手册

MySQL 8.4 参考手册  /  ...  /  原子数据定义语句支持

15.1.1 原子数据定义语句支持

MySQL 8.4 支持原子数据定义语言 (DDL) 语句。此功能称为原子 DDL。原子 DDL 语句将与 DDL 操作关联的数据字典更新、存储引擎操作和二进制日志写入组合到单个原子操作中。该操作要么提交,并将适用的更改持久化到数据字典、存储引擎和二进制日志中,要么回滚,即使服务器在操作期间停止也是如此。

注意

原子 DDL 不是事务性 DDL。DDL 语句(原子性或其他)隐式结束当前会话中处于活动状态的任何事务,就好像您在执行该语句之前执行了 COMMIT 一样。这意味着 DDL 语句不能在另一个事务中执行,不能在事务控制语句(例如 START TRANSACTION ... COMMIT)中执行,也不能与同一事务中的其他语句组合使用。

MySQL 数据字典提供了集中式事务性元数据存储,从而使原子 DDL 成为可能。

本节以下主题介绍了原子 DDL 功能:

支持的 DDL 语句

原子 DDL 功能支持表和非表 DDL 语句。与表相关的 DDL 操作需要存储引擎支持,而非表 DDL 操作则不需要。目前,只有 InnoDB 存储引擎支持原子 DDL。

  • 支持的表 DDL 语句包括用于数据库、表空间、表和索引的 CREATEALTERDROP 语句,以及 TRUNCATE TABLE 语句。

  • 支持的非表 DDL 语句包括:

    • CREATEDROP 语句,以及适用的情况下,用于存储程序、触发器、视图和可加载函数的 ALTER 语句。

    • 帐户管理语句:用于用户和角色的 CREATEALTERDROP 和(如果适用)RENAME 语句,以及 GRANTREVOKE 语句。

原子 DDL 功能不支持以下语句:

原子 DDL 特性

原子 DDL 语句的特性包括以下内容:

  • 元数据更新、二进制日志写入和存储引擎操作(如果适用)组合成单个原子操作。

  • 在 DDL 操作期间,SQL 层没有中间提交。

  • 在适用的情况下:

    • 数据字典、例程、事件和可加载函数缓存的状态与 DDL 操作的状态一致,这意味着缓存会更新以反映 DDL 操作是成功完成还是回滚。

    • DDL 操作中涉及的存储引擎方法不执行中间提交,并且存储引擎将自身注册为 DDL 操作的一部分。

    • 存储引擎支持 DDL 操作的重做和回滚,这在 DDL 操作的“*后 DDL*”阶段执行。

  • DDL 操作的可见行为是原子的。

存储引擎支持

目前,只有 InnoDB 存储引擎支持原子 DDL。不支持原子 DDL 的存储引擎不受 DDL 原子性的约束。涉及免除存储引擎的 DDL 操作仍然可能导致在操作中断或仅部分完成时可能出现的不一致。

为了支持 DDL 操作的重做和回滚,InnoDB 将 DDL 日志写入 mysql.innodb_ddl_log 表,这是一个隐藏的数据字典表,驻留在 mysql.ibd 数据字典表空间中。

要查看在 DDL 操作期间写入 mysql.innodb_ddl_log 表的 DDL 日志,请启用 innodb_print_ddl_logs 配置选项。有关更多信息,请参阅查看 DDL 日志

注意

mysql.innodb_ddl_log 表所做更改的重做日志会立即刷新到磁盘,而不管 innodb_flush_log_at_trx_commit 设置如何。立即刷新重做日志可以避免出现 DDL 操作修改了数据文件,但由这些操作导致的对 mysql.innodb_ddl_log 表所做更改的重做日志未持久保存到磁盘的情况。这种情况可能会在回滚或恢复期间导致错误。

InnoDB 存储引擎分阶段执行 DDL 操作。诸如 ALTER TABLE 之类的 DDL 操作可以在“*提交*”阶段之前多次执行“*准备*”和“*执行*”阶段。

  1. *准备*:创建所需的对象并将 DDL 日志写入 mysql.innodb_ddl_log 表。DDL 日志定义了如何前滚和回滚 DDL 操作。

  2. *执行*:执行 DDL 操作。例如,为 CREATE TABLE 操作执行创建例程。

  3. *提交*:更新数据字典并提交数据字典事务。

  4. *后 DDL*:从 mysql.innodb_ddl_log 表中重放和删除 DDL 日志。为了确保可以安全地执行回滚而不会导致不一致,文件操作(例如重命名或删除数据文件)在此最终阶段执行。此阶段还会从 mysql.innodb_dynamic_metadata 数据字典表中删除动态元数据,用于 DROP TABLETRUNCATE TABLE 和其他重建表的 DDL 操作。

在“*后 DDL*”阶段,无论 DDL 操作是提交还是回滚,都会从 mysql.innodb_ddl_log 表中重放和删除 DDL 日志。仅当服务器在 DDL 操作期间停止时,DDL 日志才应保留在 mysql.innodb_ddl_log 表中。在这种情况下,DDL 日志将在恢复后重放和删除。

在恢复情况下,当服务器重新启动时,DDL 操作可能会提交或回滚。如果在 DDL 操作的“*提交*”阶段执行的数据字典事务出现在重做日志和二进制日志中,则该操作被视为成功并被前滚。否则,当 InnoDB 重放数据字典重做日志时,不完整的数据字典事务将回滚,并且 DDL 操作也将回滚。

查看 DDL 日志

要查看在涉及 InnoDB 存储引擎的原子 DDL 操作期间写入 mysql.innodb_ddl_log 数据字典表的 DDL 日志,请启用 innodb_print_ddl_logs 以使 MySQL 将 DDL 日志写入 stderr。根据主机操作系统和 MySQL 配置,stderr 可能是错误日志、终端或控制台窗口。请参阅第 7.4.2.2 节“默认错误日志目标配置”

InnoDB 将 DDL 日志写入 mysql.innodb_ddl_log 表,以支持 DDL 操作的重做和回滚。mysql.innodb_ddl_log 表是一个隐藏的数据字典表,驻留在 mysql.ibd 数据字典表空间中。与其他隐藏的数据字典表一样,mysql.innodb_ddl_log 表无法在非调试版本的 MySQL 中直接访问。(请参阅第 16.1 节“数据字典架构”。)mysql.innodb_ddl_log 表的结构对应于以下定义:

CREATE TABLE mysql.innodb_ddl_log (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  thread_id BIGINT UNSIGNED NOT NULL,
  type INT UNSIGNED NOT NULL,
  space_id INT UNSIGNED,
  page_no INT UNSIGNED,
  index_id BIGINT UNSIGNED,
  table_id BIGINT UNSIGNED,
  old_file_path VARCHAR(512) COLLATE utf8mb4_bin,
  new_file_path VARCHAR(512) COLLATE utf8mb4_bin,
  KEY(thread_id)
);
  • id:DDL 日志记录的唯一标识符。

  • thread_id:每个 DDL 日志记录都分配有一个 thread_id,用于重放和删除属于特定 DDL 操作的 DDL 日志。涉及多个数据文件操作的 DDL 操作会生成多个 DDL 日志记录。

  • type:DDL 操作类型。类型包括 FREE(删除索引树)、DELETE(删除文件)、RENAME(重命名文件)或 DROP(从 mysql.innodb_dynamic_metadata 数据字典表中删除元数据)。

  • space_id:表空间 ID。

  • page_no:包含分配信息的页面;例如,索引树根页面。

  • index_id:索引 ID。

  • table_id:表 ID。

  • old_file_path:旧的表空间文件路径。由创建或删除表空间文件的 DDL 操作使用;也由重命名表空间的 DDL 操作使用。

  • new_file_path:新的表空间文件路径。由重命名表空间文件的 DDL 操作使用。

此示例演示了如何启用 innodb_print_ddl_logs 以查看为 CREATE TABLE 操作写入 strderr 的 DDL 日志。

mysql> SET GLOBAL innodb_print_ddl_logs=1;
mysql> CREATE TABLE t1 (c1 INT) ENGINE = InnoDB;
[Note] [000000] InnoDB: DDL log insert : [DDL record: DELETE SPACE, id=18, thread_id=7,
space_id=5, old_file_path=./test/t1.ibd]
[Note] [000000] InnoDB: DDL log delete : by id 18
[Note] [000000] InnoDB: DDL log insert : [DDL record: REMOVE CACHE, id=19, thread_id=7,
table_id=1058, new_file_path=test/t1]
[Note] [000000] InnoDB: DDL log delete : by id 19
[Note] [000000] InnoDB: DDL log insert : [DDL record: FREE, id=20, thread_id=7,
space_id=5, index_id=132, page_no=4]
[Note] [000000] InnoDB: DDL log delete : by id 20
[Note] [000000] InnoDB: DDL log post ddl : begin for thread id : 7
[Note] [000000] InnoDB: DDL log post ddl : end for thread id : 7