15.1.1 原子数据定义语言支持
MySQL 8.4 支持原子数据定义语言(DDL)语句。这一特性称为原子 DDL。原子 DDL 语句将数据字典更新、存储引擎操作和二进制日志写入组合成一个单个的原子操作。如果服务器在执行操作时崩溃,操作将被回滚,相关变化将不被持久化到数据字典、存储引擎和二进制日志中。
原子 DDL 不是事务性 DDL。无论是原子还是非原子,DDL 语句都会隐式地结束当前会话中的活动事务,就像你执行了COMMIT
一样。这意味着,DDL 语句不能在另一个事务中执行,也不能与其他语句组合在同一个事务中。
原子 DDL 是由 MySQL 数据字典提供的-centralized、事务性元数据存储所可能的。
本节下面部分介绍了原子 DDL 的相关内容:
原子DDL功能支持表和非表DDL语句。表相关DDL操作需要存储引擎支持,而非表DDL操作不需要。当前,只有InnoDB
存储引擎支持原子DDL。
-
支持的表DDL语句包括:
-
Supported non-table DDL statements include:
-
CREATE
、ALTER
和DROP
语句,以及TRUNCATE TABLE
语句。 -
支持的非表DDL语句包括:
-
原子DDL功能不支持的语句:
-
涉及到除
InnoDB
存储引擎以外的表相关DDL语句。 -
CREATE SERVER
,ALTER SERVER
, 和DROP SERVER
语句。
原子DDL语句的特点包括:
-
元数据更新、binary日志写入和存储引擎操作(如果适用)被组合成一个原子操作。
-
在DDL操作过程中,SQL层面没有中间提交。
-
如果适用:
-
数据字典、存储程序、事件和可加载函数缓存的状态与DDL操作的状态一致,意味着缓存会更新以反映DDL操作是否成功或回滚。
-
在DDL操作中涉及到的存储引擎方法不执行中间提交,存储引擎将自己注册为DDL操作的一部分。
-
存储引擎支持redo和回滚DDL操作,这是在DDL操作的Post-DDL阶段进行的。
-
-
DDL操作的可见行为是原子性的。
当前,只有InnoDB
存储引擎支持原子DDL。不能支持原子DDL的存储引擎除外,执行DDL操作时可能会出现中断或部分完成引起的一致性问题。
为了支持redo和回滚DDL操作,InnoDB
将DDL日志写入到mysql.innodb_ddl_log
表中,这是一个隐藏的数据字典表,位于mysql.ibd
数据字典表空间。
要查看在DDL操作期间写入到mysql.innodb_ddl_log
表中的DDL日志,请启用innodb_print_ddl_logs
配置选项。更多信息,见查看DDL日志。
对mysql.innodb_ddl_log
表的redo日志,总是立即写入磁盘,不管innodb_flush_log_at_trx_commit
设置。立即写入redo日志避免了数据文件由DDL操作修改,但对mysql.innodb_ddl_log
表的redo日志未持久化到磁盘的情况,这种情况可能会在回滚或恢复时出现错误。
InnoDB存储引擎以阶段执行DDL操作,例如ALTER TABLE
可能在Prepare和Perform阶段重复执行多次,直到Commit阶段。
-
Prepare:创建必要对象,并将DDL日志写入
mysql.innodb_ddl_log
表。DDL日志定义如何回滚和恢复DDL操作。 -
Perform:执行DDL操作,例如创建一个
CREATE TABLE
操作。 -
Commit:更新数据字典并提交数据字典事务。
-
Post-DDL:从
mysql.innodb_ddl_log
表中回放和删除DDL日志。为了确保回滚操作安全地执行,不会引入不一致,文件操作,如重命名或删除数据文件,在这个最后阶段执行。这阶段还将DROP TABLE
、TRUNCATE TABLE
等DDL操作重建表的动态元数据从mysql.innodb_dynamic_metadata
数据字典表中删除。
在Post-DDL阶段,DDL日志将从mysql.innodb_ddl_log
表中回放和删除,不管DDL操作是否已提交或回滚。只有在服务器在DDL操作过程中崩溃时,DDL日志才会留存在mysql.innodb_ddl_log
表中。在这种情况下,DDL日志将在恢复后回放和删除。
在恢复情况下,DDL操作可能在服务器重启时提交或回滚。如果数据字典事务在DDL操作的Commit阶段存在于redo日志和二进制日志中,则操作被认为是成功的,并且回滚。否则,InnoDB
回放数据字典redo日志时,事务回滚,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
表,以支持redo和回滚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
查看DDL日志写入到 strderr
的 CREATE TABLE
操作。
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