MySQL 8.4 支援原子資料定義語言 (DDL) 語句。此功能稱為原子 DDL。原子 DDL 語句將與 DDL 操作相關的資料字典更新、儲存引擎操作和二進制日誌寫入結合到單一原子操作中。該操作會被提交,將適用的變更持久化到資料字典、儲存引擎和二進制日誌,或者被回滾,即使伺服器在操作期間停止也是如此。
原子 DDL 不是事務性 DDL。DDL 語句,無論是否為原子,都會隱式結束目前工作階段中任何處於活動狀態的事務,如同在執行語句之前執行了 COMMIT
一樣。這表示 DDL 語句不能在另一個事務中執行,也不能在諸如 START TRANSACTION ... COMMIT
之類的事務控制語句中執行,也不能與同一事務中的其他語句合併使用。
原子性 DDL 得益於 MySQL 資料字典,它提供了集中式的、具交易性的中繼資料儲存。
本節將在以下主題中描述原子性 DDL 功能
原子性 DDL 功能同時支援資料表和非資料表的 DDL 語法。與資料表相關的 DDL 操作需要儲存引擎的支援,而與非資料表相關的 DDL 操作則不需要。目前,只有 InnoDB
儲存引擎支援原子性 DDL。
支援的資料表 DDL 語法包括用於資料庫、表空間、資料表和索引的
CREATE
、ALTER
和DROP
語法,以及TRUNCATE TABLE
語法。支援的非資料表 DDL 語法包括
原子性 DDL 功能不支援以下語法
涉及
InnoDB
以外的儲存引擎的資料表相關 DDL 語法。
原子性 DDL 語法的特性包括以下內容
中繼資料更新、二進位日誌寫入和儲存引擎操作(如適用)會合併為單一原子性操作。
在 DDL 操作期間,SQL 層沒有中間提交。
如適用
資料字典、常式、事件和可載入函式快取的狀態與 DDL 操作的狀態一致,表示快取會更新以反映 DDL 操作是否成功完成或回滾。
DDL 操作中涉及的儲存引擎方法不會執行中間提交,而且儲存引擎會將自身註冊為 DDL 操作的一部分。
儲存引擎支援 DDL 操作的重做和回滾,這會在 DDL 操作的Post-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 紀錄。
無論 innodb_flush_log_at_trx_commit
設定為何,對 mysql.innodb_ddl_log
資料表的變更所產生的重做日誌都會立即刷新到磁碟。立即刷新重做日誌可避免因 DDL 操作修改資料檔案,但這些操作對 mysql.innodb_ddl_log
資料表所產生的變更重做日誌卻未持續保存到磁碟的情況。這種情況可能會在回滾或復原期間導致錯誤。
InnoDB
儲存引擎會分階段執行 DDL 操作。DDL 操作(例如 ALTER TABLE
)可能會在 Commit 階段之前多次執行準備 (Prepare)和執行 (Perform)階段。
準備 (Prepare):建立所需的物件,並將 DDL 紀錄寫入
mysql.innodb_ddl_log
資料表。DDL 紀錄定義了如何向前和向後回滾 DDL 操作。執行 (Perform):執行 DDL 操作。例如,針對
CREATE TABLE
操作執行建立常式。Commit:更新資料字典並提交資料字典交易。
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 紀錄才會保留在 mysql.innodb_ddl_log
資料表中。在這種情況下,DDL 紀錄會在復原後重播和移除。
在復原情況下,當伺服器重新啟動時,可能會提交或回滾 DDL 操作。如果 DDL 操作的Commit階段期間執行的資料字典交易出現在重做日誌和二進位日誌中,則該操作會被視為成功並向前推進。否則,當 InnoDB
重播資料字典重做日誌時,不完整的資料字典交易將會被回滾,而 DDL 操作也會被回滾。
若要檢視在原子性 DDL 操作期間寫入 mysql.innodb_ddl_log
資料字典資料表(涉及 InnoDB
儲存引擎)的 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 中無法直接存取 mysql.innodb_ddl_log
資料表。(請參閱第 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
,以檢視寫入 stderr
的 CREATE TABLE
操作的 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