MySQL 8.0 では、アトミックデータ定義言語 (DDL) ステートメントがサポートされます。 この機能は、アトミック DDLと呼ばれます。 アトミック DDL ステートメントは、DDL 操作に関連するデータディクショナリ更新、ストレージエンジン操作およびバイナリログ書き込みを単一のアトミックトランザクションとして結び付けます。 操作は、データディクショナリ、ストレージエンジンおよびバイナリログに適用可能な変更を保持してコミットされるか、操作中にサーバーが停止した場合でもロールバックされます。
アトミック DDLはトランザクション DDLではありません。 DDL ステートメント、アトミックまたはそれ以外の場合は、ステートメントを実行する前に COMMIT
を実行したかのように、現在のセッションでアクティブなトランザクションを暗黙的に終了します。 つまり、DDL ステートメントは、別のトランザクション内、START TRANSACTION ... COMMIT
などのトランザクション制御ステートメント内、または同じトランザクション内の他のステートメントと組み合せることはできません。
アトミック DDL は、MySQL 8.0 で MySQL データディクショナリを導入することで可能になります。 以前の MySQL バージョンでは、メタデータはメタデータファイル、非トランザクションテーブル、および中間コミットを必要とするストレージエンジン固有のディクショナリに格納されていました。 MySQL データディクショナリによって提供される集中化されたトランザクションメタデータ記憶域により、このバリアが削除され、DDL ステートメントの操作をアトミックに再構築できるようになりました。
アトミック DDL 機能については、このセクションの次のトピックで説明します:
アトミック DDL 機能では、テーブル DDL ステートメントと非テーブル DDL ステートメントの両方がサポートされています。 テーブル関連の DDL 操作ではストレージエンジンのサポートが必要ですが、テーブル以外の DDL 操作では必要ありません。 現在、InnoDB
ストレージエンジンのみがアトミック DDL をサポートしています。
サポートされているテーブル DDL ステートメントには、データベース、テーブルスペース、テーブルおよびインデックスに対する
CREATE
、ALTER
およびDROP
ステートメントと、TRUNCATE TABLE
ステートメントが含まれます。-
サポートされているテーブル以外の DDL ステートメントは次のとおりです:
CREATE
ステートメントとDROP
ステートメント、および該当する場合はストアドプログラム、トリガー、ビューおよびユーザー定義関数 (UDF) のALTER
ステートメント。アカウント管理ステートメント:
CREATE
,ALTER
,DROP
、および該当する場合は、ユーザーおよびロール用のRENAME
ステートメントと、GRANT
およびREVOKE
ステートメント。
次のステートメントは、アトミック DDL 機能ではサポートされていません:
InnoDB
以外のストレージエンジンを含むテーブル関連の DDL ステートメント。INSTALL PLUGIN
およびUNINSTALL PLUGIN
ステートメント。INSTALL COMPONENT
およびUNINSTALL COMPONENT
ステートメント。CREATE SERVER
、ALTER SERVER
およびDROP SERVER
ステートメント。
アトミック DDL ステートメントの特性は次のとおりです:
メタデータの更新、バイナリログの書き込み、およびストレージエンジンの操作 (該当する場合) は、単一の原子性操作に結合されます。
DDL 操作中、SQL レイヤーに中間コミットはありません。
-
該当する場合:
データディクショナリ、ルーチン、イベントおよび UDF キャッシュの状態は、DDL 操作のステータスと一貫性があります。つまり、DDL 操作が正常に完了したかロールバックされたかを反映するようにキャッシュが更新されます。
DDL 操作に関連するストレージエンジンのメソッドは中間コミットを実行せず、ストレージエンジンはそれ自体を DDL 操作の一部として登録します。
ストレージエンジンは、DDL 操作の Post-DDL フェーズで実行される DDL 操作の redo およびロールバックをサポートしています。
DDL 操作の表示可能な動作はアトミックで、一部の DDL ステートメントの動作が変更されます。 DDL ステートメントの動作の変更を参照してください。
このセクションでは、アトミック DDL サポートの導入による DDL ステートメントの動作の変更について説明します。
-
すべての名前付きテーブルがアトミック DDL でサポートされているストレージエンジンを使用している場合、
DROP TABLE
操作は完全にアトミックです。 このステートメントは、すべてのテーブルを正常に削除するか、ロールバックします。指定されたテーブルが存在せず、ストレージエンジンに関係なく変更が行われない場合、
DROP TABLE
はエラーで失敗します。 次の例では、指定したテーブルが存在しないために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)
注記この動作の変更のため、MySQL 8.0 レプリカにレプリケートすると、MySQL 5.7 レプリケーションソースサーバーで部分的に完了した
DROP TABLE
ステートメントが失敗します。 この失敗のシナリオを回避するには、DROP TABLE
ステートメントでIF EXISTS
構文を使用して、存在しないテーブルに対してエラーが発生しないようにします。 すべてのテーブルがアトミック DDL でサポートされているストレージエンジンを使用する場合、
DROP DATABASE
はアトミックです。 このステートメントは、すべてのオブジェクトを正常に削除するか、ロールバックします。 ただし、ファイルシステムからのデータベースディレクトリの削除は最後に行われ、アトミック操作の一部ではありません。 ファイルシステムエラーまたはサーバーの停止が原因でデータベースディレクトリの削除に失敗した場合、DROP DATABASE
トランザクションはロールバックされません。アトミック DDL でサポートされているストレージエンジンを使用しないテーブルの場合、テーブルの削除はアトミック
DROP TABLE
またはDROP DATABASE
トランザクションの外部で行われます。 このようなテーブルの削除はバイナリログに個別に書き込まれるため、DROP TABLE
またはDROP DATABASE
操作が中断された場合は、ストレージエンジン、データディクショナリ、およびバイナリログ間の相違が最大で 1 つのテーブルに制限されます。 複数のテーブルを削除する操作の場合、アトミック DDL でサポートされているストレージエンジンを使用しないテーブルは、それを実行するテーブルの前に削除されます。アトミック DDL でサポートされているストレージエンジンを使用するテーブルに対する
CREATE TABLE
,ALTER TABLE
,RENAME TABLE
,TRUNCATE TABLE
,CREATE TABLESPACE
、およびDROP TABLESPACE
操作は、その操作中にサーバーが停止すると、完全にコミットまたはロールバックされます。 以前の MySQL リリースでは、これらの操作が中断されると、ストレージエンジン、データディクショナリ、およびバイナリログ間の不一致が発生したり、孤立したファイルの背後に残されたりする可能性がありました。RENAME TABLE
操作は、すべての名前付きテーブルがアトミック DDL でサポートされているストレージエンジンを使用している場合にのみアトミックです。-
MySQL 8.0.21 の時点では、アトミック DDL をサポートするストレージエンジンでは、行ベースレプリケーションが使用されているときに、
CREATE TABLE ... SELECT
ステートメントがバイナリログに 1 つのトランザクションとして記録されます。 以前は、2 つのトランザクションとしてログに記録されていました。1 つはテーブルの作成用、もう 1 つはデータの挿入用です。 2 つのトランザクション間またはデータの挿入中にサーバー障害が発生すると、空のテーブルがレプリケーションされる可能性があります。 アトミック DDL サポートの導入により、CREATE TABLE ... SELECT
ステートメントは行ベースレプリケーションに対して安全であり、GTID ベースレプリケーションでの使用が許可されるようになりました。アトミック DDL 制約と外部キー制約の両方をサポートするストレージエンジンでは、行ベースレプリケーションが使用されている場合、
CREATE TABLE ... SELECT
ステートメントで外部キーの作成は許可されません。 外部キー制約は、後でALTER TABLE
を使用して追加できます。CREATE TABLE ... SELECT
がアトミック操作として適用されると、データの挿入中にメタデータロックがテーブルに保持され、操作中にテーブルへの同時アクセスが防止されます。 -
名前付きビューが存在せず、変更が行われない場合、
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)
注記この動作の変更のため、MySQL 8.0 レプリカでレプリケートすると、MySQL 5.7 レプリケーションソースサーバーで部分的に完了した
DROP VIEW
操作が失敗します。 この失敗のシナリオを回避するには、DROP VIEW
ステートメントでIF EXISTS
構文を使用して、存在しないビューに対してエラーが発生しないようにします。 -
アカウント管理ステートメントの部分実行は許可されなくなりました。 アカウント管理ステートメントは、指定されたすべてのユーザーに対して成功するか、ロールバックされ、エラーが発生しても効果はありません。 以前の MySQL バージョンでは、複数のユーザーを指定するアカウント管理ステートメントは、一部のユーザーでは成功し、他のユーザーでは失敗する可能性がありました。
この例では、動作の変更が示されています。この例では、2 番目の
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 が導入される前に、2 番目の
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 8.0 レプリカでレプリケートすると、MySQL 5.7 レプリケーションソースサーバーで部分的に完了したアカウント管理ステートメントが失敗します。 この失敗のシナリオを回避するには、アカウント管理ステートメントで必要に応じて
IF EXISTS
またはIF NOT EXISTS
構文を使用して、名前付きユーザーに関連するエラーを防止します。
現在、InnoDB
ストレージエンジンのみがアトミック DDL をサポートしています。 アトミック DDL をサポートしないストレージエンジンは、DDL アトミック性から除外されます。 除外されたストレージエンジンに関連する DDL 操作は、操作が中断されたとき、または部分的にしか完了しなかったときに発生する可能性のある不整合を引き起こすことができます。
DDL 操作の redo およびロールバックをサポートするために、InnoDB
は、mysql.ibd
データディクショナリテーブルスペースに存在する非表示のデータディクショナリテーブルである mysql.innodb_ddl_log
テーブルに DDL ログを書き込みます。
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
などの DDL 操作では、Commit フェーズの前に準備および Perform フェーズを複数回実行できます。
準備: 必要なオブジェクトを作成し、DDL ログを
mysql.innodb_ddl_log
テーブルに書き込みます。 DDL ログは、DDL 操作をロールフォワードおよびロールバックする方法を定義します。Perform: DDL 操作を実行します。 たとえば、
CREATE TABLE
操作の作成ルーチンを実行します。Commit: データディクショナリを更新し、データディクショナリのトランザクションをコミットします。
Post-DDL: DDL ログをリプレイし、
mysql.innodb_ddl_log
テーブルから削除します。 不整合を引き起こさずにロールバックを安全に実行できるように、データファイルの名前変更や削除などのファイル操作はこの最終フェーズで実行されます。 このフェーズでは、DROP TABLE
、TRUNCATE TABLE
およびテーブルを再構築するその他の DDL 操作のために、mysql.innodb_dynamic_metadata
データディクショナリテーブルから動的メタデータも削除します。
DDL 操作がコミットされているかロールバックされているかに関係なく、Post-DDL フェーズ中に DDL ログがリプレイされ、mysql.innodb_ddl_log
テーブルから削除されます。 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 に stderr
への DDL ログの書込みを許可します。 ホストのオペレーティングシステムおよび MySQL の構成によっては、stderr
がエラーログ、端末またはコンソールウィンドウである場合があります。 セクション5.4.2.2「デフォルトのエラーログ保存先の構成」を参照してください。
InnoDB
は、DDL 操作の redo およびロールバックをサポートするために、DDL ログを mysql.innodb_ddl_log
テーブルに書き込みます。 mysql.innodb_ddl_log
テーブルは、mysql.ibd
データディクショナリテーブルスペースに存在する非表示のデータディクショナリテーブルです。 他の非表示のデータディクショナリテーブルと同様に、MySQL の非デバッグバージョンでは mysql.innodb_ddl_log
テーブルに直接アクセスできません。 (セクション14.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 UTF8_BIN,
new_file_path VARCHAR(512) COLLATE UTF8_BIN,
KEY(thread_id)
);
id
: DDL ログレコードの一意の識別子。thread_id
: 各 DDL ログレコードには、特定の DDL 操作に属する DDL ログのリプレイおよび削除に使用されるthread_id
が割り当てられます。 複数のデータファイル操作を含む 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 操作で使用されます。
この例では、CREATE TABLE
操作のために strderr
に書き込まれた DDL ログを innodb_print_ddl_logs
で表示できるようにする方法を示します。
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