1. DDLアトミック性の概要 8.0 より前は、統一されたデータ ディクショナリ dd はありませんでした。サーバー レイヤーとエンジン レイヤーにはそれぞれメタデータ セットがありました。サーバー レイヤーのメタデータには (.frm、.opt、.par、.trg など) が含まれ、テーブル定義、パーティション テーブル定義、トリガー定義などの情報を格納するために使用されていました。また、innodb レイヤーにも、テーブル情報、インデックス情報などを含む独自のメタデータ セットがありました。これら 2 つのメタデータ セットの一貫性を保証するメカニズムがなかったため、異常な状況下でメタデータの不一致の問題が発生しました。典型的なシナリオでは、テーブル削除操作で、サーバー レイヤーの frm は正常に削除されましたが、エンジン レイヤーのデータ ディクショナリは更新されず、同じ名前のテーブルの再構築に失敗しました。同様に、たとえばテーブル t1、t2 を削除すると、t2 はまだ存在するのに t1 だけが削除されるなどの問題が発生する可能性があります。 8.0 の重要なタスクは、データ ディクショナリを統合し、DD (データ ディクショナリ) モジュールを分離し、サーバー レイヤーのメタデータを廃止し、InnoDB のメタデータをサーバー レイヤーと InnoDB レイヤーで共通に使用できる DD インターフェイスに抽象化することです。 DD に基づいて、DDL のアトミック性機能が導入され、DDL 操作が完全に実行されるか、まったく実行されないかのいずれかが保証されます。このロジックを実装するための鍵は、DD データ ディクショナリの変更、エンジン レイヤーの変更 (ファイルの作成、テーブル スペースの初期化、btree の作成など)、binlog の書き込みなど、DDL に関連するすべての変更を「トランザクション」として扱い、トランザクションのアトミック性を使用して DDL 操作のアトミック性を保証することです。 2. DDLアトミック性実装の原則 アトミック性を実現するための鍵は、DD データ ディクショナリの変更、エンジン レイヤーの変更、および binlog の書き込みが 1 つのトランザクションになるようにすることです。 MySQL の既存の XA トランザクション メカニズムは、DML トランザクションと binlog の一貫性を効果的に保証できます。 DDL データ ディクショナリも InnoDB エンジンによって保存されるため、DD データ ディクショナリの変更を binlog と一致させることは簡単です。次に解決する必要があるもう 1 つの問題は、DD データ ディクショナリとエンジン レイヤーの変更の一貫性です。ファイルの作成、ファイル名の変更、キャッシュのクリアなど、エンジン レイヤーでのすべての変更が redo に記録されるわけではなく、XA メカニズムでは単純に解決できません。そのため、8.0 では DDL_LOG メカニズムも導入されています。具体的には、REDO を記録しない一部の操作は、ログの形式で ddl_log テーブルに書き込まれます。このテーブルは InnoDB エンジン テーブルです。ddl_log データが DD データ ディクショナリの変更と一貫していることを保証することで、DD データ ディクショナリの変更、エンジン レイヤーの変更、および binlog 書き込みの一貫性の問題が最終的に解決されます。 3. DD導入前後の比較
4.DDL操作実装ロジック ddl_log テーブルの導入後、DDL 操作は元のベースからいくつか変更されました。主なポイントは 2 つあります。1 つは、DDL の実行中に、DDL 操作が ddl_log テーブルに記録されることです。もう 1 つは、post_ddl ステージが追加されたことです。DDL トランザクションがコミットされた後、drop-table などのいくつかの DDL 終了アクションが実行されます。物理ファイルの実際の削除は、post-ddl ステージで行われます。 post-ddl の主な機能は、ddl-log のコンテンツを読み取って再生することです。 DDL 操作タイプは次のとおりです。 列挙型クラス Log_Type : uint32_t { /** 最小のログタイプ */ 最小ログ = 1、 /** インデックスツリーを削除する */ FREE_TREE_LOG = 1、 /** ファイルを削除する */ DELETE_SPACE_LOG、 /** ファイルの名前を変更する */ RENAME_SPACE_LOG、 /** innodb_dynamic_metadata のエントリを削除します */ DROP_LOG、 /** 辞書キャッシュ内のテーブルの名前を変更します。 */ RENAME_TABLE_LOG、 /** 辞書キャッシュからテーブルを削除します */ REMOVE_CACHE_LOG、 /** 表領域を暗号化する */ ALTER_ENCRYPT_TABLESPACE_LOG、 /** 最大のログタイプ */ BIGGEST_LOG = ALTER_ENCRYPT_TABLESPACE_LOG }; innodb_print_ddl_logs スイッチを使用すると、DDL プロセス中に innodb_ddl_log テーブルに書き込まれた内容を確認できます。以下では、いくつかの一般的な ddl 操作によって生成された ddl_logs を使用して、ddl のアトミック性を保証する方法を説明します。 4.1 テーブルの作成 ステートメント: create table dd_tt(id int primary key, c1 int); [InnoDB] DDL ログ挿入: [DDL レコード: DELETE SPACE、id=352、thread_id=23、space_id=71、old_file_path=./mysql/dd_tt.ibd] [InnoDB] DDL ログ削除: 352 [InnoDB] DDL ログ挿入: [DDL レコード: REMOVE CACHE、id=353、thread_id=23、table_id=1128、new_file_path=mysql/dd_tt] [InnoDB] DDL ログ削除: 353 [InnoDB] DDL ログ挿入: [DDL レコード: FREE、id=354、thread_id=23、space_id=71、index_id=231、page_no=4] [InnoDB] DDL ログ削除: 354 [InnoDB] DDL ログ投稿 ddl: スレッド ID: 23 の開始 [InnoDB] DDL ログ投稿 ddl: スレッド ID の終了: 23 例: 1. すべての挿入操作は個別のトランザクションであり、対応する逆削除操作は DDL トランザクション全体の一部です。 2. 挿入操作は、ファイル操作の逆の操作を記録します。たとえば、table_space を作成する場合、逆の操作は delete_space_log です。 3. DDL トランザクションが最終的に成功した場合、すべての逆削除操作も有効になり、ddl_log は正常にクリーンアップされます。実行中に DDL トランザクションが失敗した場合 (インスタンスがクラッシュした場合など)、削除操作はロールバックされ、3 つの insert_logs が ddl_log テーブルに残ります。リカバリ時に、これらの ddl_logs が再生され、DDL プロセス中に生成されたガベージがクリーンアップされます。 4. クラッシュ リカバリ中に、binlog がディスクに書き込まれ、対応する ddl トランザクションが準備状態にある場合は、最終トランザクションをコミットして ddl_log をクリーンアップする必要があります。binlog がディスクに書き込まれていない場合は、ddl トランザクションをロールバックする必要があり、ddl_log テーブルに 3 つのレコードが残ります。障害リカバリが完了したら、これらのレコードを再生する必要があります。これは、実際には、ロールバック後にクリーンであることを確認するためにファイルを作成し、btree を作成するという逆の操作です。 4.2 ドロップテーブル ステートメント: drop table dd_tt; [InnoDB] DDL ログ挿入: [DDL レコード: DROP、id=355、thread_id=23、table_id=1128] [InnoDB] DDL ログ挿入: [DDL レコード: DELETE SPACE、id=356、thread_id=23、space_id=71、old_file_path=./mysql/dd_tt.ibd] [InnoDB] DDL ログ投稿 ddl: スレッド ID: 23 の開始 [InnoDB] DDL ログ再生: [DDL レコード: DELETE SPACE、id=356、thread_id=23、space_id=71、old_file_path=./mysql/dd_tt.ibd] [InnoDB] DDL ログ再生: [DDL レコード: DROP、id=355、thread_id=23、table_id=1128] [InnoDB] DDL ログ投稿 ddl: スレッド ID の終了: 23 注意: ドロップ操作の場合、実行プロセス中に ddl_log のみが操作され、実際の物理テーブルのドロップ操作は実行されません。 post-ddl ステージでは、ddl_log テーブル内のレコードが読み取られ、再生されて実際の削除操作が実行されます。実行中にクラッシュが発生した場合、ddl_log の内容を含む ddl トランザクション全体がロールバックされ、ドロップ操作全体は実行されなかったかのようになります。 4.3 インデックスを追加 ステートメント: alter table dd_tt add index idx_c1(c1); [InnoDB] DDL ログ挿入: [DDL レコード: FREE、id=360、thread_id=23、space_id=72、index_id=233、page_no=5] [InnoDB] DDL ログ削除: 360 [InnoDB] DDL ログ投稿 ddl: スレッド ID: 23 の開始 [InnoDB] DDL ログ投稿 ddl: スレッド ID の終了: 23 注: インデックスの作成はテーブルの作成に似ています。挿入操作はトランザクションであり、個別に送信されます。削除操作は、DDL トランザクション全体の一部として記録されます。トランザクションが最終的に送信されると、DDL ログの内容は削除されます。トランザクションが最終的にロールバックされると、FREE ログが DDL ログに残ります。リプレイを使用して、作成されたインデックスをクリーンアップし、ロールバック効果を実現できます。 4.4 ドロップインデックス ステートメント: alter table dd_tt drop index idx_c1; [InnoDB] DDL ログ挿入: [DDL レコード: FREE、id=361、thread_id=23、space_id=72、index_id=233、page_no=5] [InnoDB] DDL ログ投稿 ddl: スレッド ID: 23 の開始 [InnoDB] DDL ログ再生: [DDL レコード: FREE、id=361、thread_id=23、space_id=72、index_id=233、page_no=5] [InnoDB] DDL ログ投稿 ddl: スレッド ID の終了: 23 例: drop table と同様に、実行プロセス中はログのみが記録され、実際の削除操作は post-ddl ステージで実行されます。 4.5 列を追加 ステートメント: alter table dd_tt add column c2 int; [InnoDB] DDL ログ投稿 ddl: スレッド ID: 23 の開始 [InnoDB] DDL ログ投稿 ddl: スレッド ID の終了: 23 例: 8.0 では、instant-ddl を使用して列が追加されます。instant-ddl は、dml トランザクションと同様にメタデータのみを変更し、アトミック性を保証するために ddl-log に依存しません。 4.6 ドロップカラム ステートメント: alter table dd_tt drop column c2; 声明の内訳: 1.準備段階: テーブル #sql-ib1129-2815969725 を作成します。 [InnoDB] DDL ログ挿入: [DDL レコード: DELETE SPACE、id=362、thread_id=23、space_id=73、old_file_path=./mysql/#sql-ib1129-2815969725.ibd] [InnoDB] DDL ログ削除: 362 [InnoDB] DDL ログ挿入: [DDL レコード: REMOVE CACHE、id=363、thread_id=23、table_id=1130、new_file_path=mysql/#sql-ib1129-2815969725] [InnoDB] DDL ログ削除: 363 [InnoDB] DDL ログ挿入: [DDL レコード: FREE、id=364、thread_id=23、space_id=73、index_id=234、page_no=4] [InnoDB] DDL ログ削除: 364 2.実行段階: ddl-log については何もなし 3. コミット段階: 3.1 [InnoDB] DDL ログ挿入: [DDL レコード: DROP、id=365、thread_id=23、table_id=1129] <br>[InnoDB] DDL ログ挿入: [DDL レコード: RENAME SPACE、id=366、thread_id=23、space_id=72、old_file_path=./mysql/#sql-ib1130-2815969726.ibd、new_file_path=./mysql/dd_tt.ibd] [InnoDB] DDL ログ削除: 366 [InnoDB] DDL ログ挿入: [DDL レコード: RENAME TABLE、id=367、thread_id=23、table_id=1129、old_file_path=mysql/#sql-ib1130-2815969726、new_file_path=mysql/dd_tt] [InnoDB] DDL ログ削除: 367 逆の操作: 3.2 テーブル #sql-ib1129-2815969725 を変更し、名前を dd_tt に変更します。 [InnoDB] DDL ログ挿入: [DDL レコード: RENAME SPACE、id=368、thread_id=23、space_id=73、old_file_path=./mysql/dd_tt.ibd、new_file_path=./mysql/#sql-ib1129-2815969725.ibd] [InnoDB] DDL ログ削除: 368 [InnoDB] DDL ログ挿入: [DDL レコード: RENAME TABLE、id=369、thread_id=23、table_id=1130、old_file_path=mysql/dd_tt、new_file_path=mysql/#sql-ib1129-2815969725] [InnoDB] DDL ログ削除: 369 逆の操作: alter [InnoDB] DDL ログ挿入: [DDL レコード: RENAME SPACE、id=368、thread_id=23、space_id=73、old_file_path=./mysql/dd_tt.ibd、new_file_path=./mysql/#sql-ib1129-2815969725.ibd] [InnoDB] DDL ログ削除: 368 [InnoDB] DDL ログ挿入: [DDL レコード: RENAME TABLE、id=369、thread_id=23、table_id=1130、old_file_path=mysql/dd_tt、new_file_path=mysql/#sql-ib1129-2815969725] [InnoDB] DDL ログ削除: 369 post-ddl フェーズでは操作のみが記録され、クリーンアップされます。 DDL後の段階: テーブル #sql-ib1130-2815969726 を削除します。 [InnoDB] DDL ログ挿入: [DDL レコード: RENAME SPACE、id=368、thread_id=23、space_id=73、old_file_path=./mysql/dd_tt.ibd、new_file_path=./mysql/#sql-ib1129-2815969725.ibd] [InnoDB] DDL ログ削除: 368 [InnoDB] DDL ログ挿入: [DDL レコード: RENAME TABLE、id=369、thread_id=23、table_id=1130、old_file_path=mysql/dd_tt、new_file_path=mysql/#sql-ib1129-2815969725] [InnoDB] DDL ログ削除: 369 注: 列の削除はコピー タイプの DDL です。基本的なロジックは、一時テーブルを作成し、データをコピーしてから、名前変更操作を実行することです。主に4つの段階があります。 1.準備段階: 一時テーブルを作成するプロセスは、テーブル作成プロセスの DDL ログ操作に似ています。挿入ログは別のトランザクションとして直接送信され、削除ログはトランザクション全体の一部です。 この段階で例外が発生した場合、逆操作レコードが ddl-log テーブルに残り、クラッシュ回復時の再生時にクリーンアップできます。 2. 実行フェーズ: データのコピーが完了し、オンライン DDL ロジックが実装されます。 3. データをコピーした後、テーブル名を変更するために名前変更操作を実行する必要があります。 1) DROP、一時テーブルを削除する 2)スペース/テーブルの名前を変更する ./mysql/#sql-ib1130-2815969726.ibd を dd_tt.idb に名前変更します。 3)スペース/テーブルの名前を変更 dd_tt.idb を /#sql-ib1129-2815969725.idb に名前変更します。 4) 古いテーブル sql-ib1130-2815969726.ibd を削除する操作を記録し、DDL 後の段階で実際の削除を実行します。 この段階で例外が発生した場合、同じ挿入ログは別のトランザクションとなり、削除はトランザクション全体の一部となります。挿入ログは ddl-log テーブルに残ります。再生によってクリーンアップし、dd_tt データを復元し、一時テーブル #sql-ib1130-2815969726.ibd をクリーンアップできます。 4. DDL後の段階: 1). 古いファイル ./mysql/#sql-ib1130-2815969726.ibd を物理的に削除します。 2). mysql.innodb_dynamic_metadata 内の関連情報をクリーンアップします。 なお、ddl-log テーブルに格納されている内容は実際には逆順に操作されるため、ddl-log を収集する場合も実際には逆順に収集され、再生されます。 4.7 テーブルを切り捨てる ステートメント: 声明の内訳: 1.dd_ttの名前を#sql-ib1130-2815969727に変更します。 [InnoDB] DDL ログ挿入: [DDL レコード: RENAME SPACE、id=372、thread_id=23、space_id=73、old_file_path=./mysql/#sql-ib1130-2815969727.ibd、new_file_path=./mysql/dd_tt.ibd [InnoDB] DDL ログ削除: 372 2. テーブル #sql-ib1130-2815969727; を削除します。 [InnoDB] DDL ログ挿入: [DDL レコード: DROP、id=373、thread_id=23、table_id=1130] [InnoDB] DDL ログ挿入: [DDL レコード: DELETE SPACE、id=374、thread_id=23、space_id=73、old_file_path=./mysql/#sql-ib1130-2815969727.ibd] 3. テーブルdd_ttを作成します。 [InnoDB] DDL ログ挿入: [DDL レコード: DELETE SPACE、id=375、thread_id=23、space_id=74、old_file_path=./mysql/dd_tt.ibd] [InnoDB] DDL ログ削除: 375 [InnoDB] DDL ログ挿入: [DDL レコード: REMOVE CACHE、id=376、thread_id=23、table_id=1131、new_file_path=mysql/dd_tt] [InnoDB] DDL ログ削除: 376 [InnoDB] DDL ログ挿入: [DDL レコード: FREE、id=377、thread_id=23、space_id=74、index_id=235、page_no=4] [InnoDB] DDL ログ削除: 377 [InnoDB] DDL ログ投稿 ddl: スレッド ID: 23 の開始 [InnoDB] DDL ログ再生: [DDL レコード: DELETE SPACE、id=374、thread_id=23、space_id=73、old_file_path=./mysql/#sql-ib1130-2815969727.ibd] [InnoDB] DDL ログ再生: [DDL レコード: DROP、id=373、thread_id=23、table_id=1130] [InnoDB] DDL ログ投稿 ddl: スレッド ID の終了: 23 例: 1. dd_tt の名前を sql-ib1130-2815969727 に変更します。 2. sql-ib1130-2815969727テーブルを削除対象としてマークします。これは、DDL後の段階で実際に削除されます。 3. 新しいテーブル dd_tt を作成します。同じ挿入操作が別のトランザクションとして送信され、削除操作はトランザクション全体の一部になります。ロールバックされた場合、挿入操作はそのまま残り、再生アクションによってクリーンアップされます。 5.DDLオペレーションコードスタック 5.1 テーブルの作成 Sql_cmd_create_table::実行 -->mysql_create_table -->mysql_create_table_no_lock -->テーブル作成の実装 --> rea_create_base_table 基本テーブルの作成 -->ha_create_table -->ha_create -->ha_innobase::create -->innobase_basic_ddl::create_impl -->create_table_info_t::create_table { ...... } --> 暗黙的なコミット -->ha_commit_trans -->MYSQL_BIN_LOG::準備 -->ha_prepare_low //すべてのトランザクションエンジンが準備する { binlog_prepare innobase_xa_prepare } -->MYSQL_BIN_LOG::コミット -->MYSQL_BIN_LOG::順序付きコミット -->MYSQL_BIN_LOG::process_flush_stage_queue -->MYSQL_BIN_LOG::flush_thread_caches -->binlog_cache_mngr::フラッシュ -->binlog_cache_data::フラッシュ -->MYSQL_BIN_LOG::write_gtid -->Log_event::書き込み -->MYSQL_BIN_LOG::Binlog_ofile::write //binlog-gtid を書き込む -->MYSQL_BIN_LOG::write_cache --> MYSQL_BIN_LOG::do_write_cache -->Binlog_cache_storage::copy_to -->ストリームコピー -->Binlog_event_writer::書き込み -->MYSQL_BIN_LOG::Binlog_ofile::write //binlog-ddl ステートメントを書き込みます -->MYSQL_BIN_LOG::sync_binlog_file -->MYSQL_BIN_LOG::プロセスコミットステージキュー -->ha_commit_low { バイナリログコミット コミット -->trx_commit_for_mysql -->trx_コミット -->trx_commit_low -->trx_commit_in_memory -->trx_undo_insert_cleanup } -->innobase_post_ddl(ht->post_ddl(thd)) -->Log_DDL::post_ddl -->スレッドIDによる再生 -->create_table_info_t::create_table -->テーブル定義を作成する -->dict_mem_table_create //辞書メモリオブジェクトとしてInnoDBメモリを構築 -->row_create_table_for_mysql -->dict_build_table_def -->dict_build_tablespace_for_table -->新しいxxx.idbファイルを作成します-->Log_DDL::write_delete_space_log { -->Log_DDL::挿入_削除_スペース_ログ -->trx_start_internal //トランザクションを内部的に開始し、個別に送信します。 -->DDL_Record (DELETE_SPACE_LOG) の構築 -->DDL_Log_Table::insert (物理 B ツリーの書き込み) -->Log_DDL:delete_by_id //ddl トランザクションの一部として ddl_log 操作を削除します。 } -->fil_ibd_create -->セグメント、エクステント、ページを初期化する -->Log_DDL::write_remove_cache_log -->Log_DDL::insert_remove_cache_log -->Log_DDL::delete_by_id -->create_index (プライマリテーブル、セカンダリインデックス) -->dict_create_index_tree_in_mem -->btr_create -->Log_DDL::write_free_tree_log -->Log_DDL::insert_free_tree_log -->Log_DDL:delete_by_id<br> クラッシュリカバリ -->ha_post_recover -->post_recover_handlerton -->innobase_post_recover -->Log_DDL::回復 -->Log_DDL::replay_all -->Log_DDL::再生 { 再生_削除_スペース_ログ 再生_キャッシュログの削除 リプレイ_フリー_ツリー_ログ ...... } -->IDで削除 -->DDL_Log_Table::削除 5.2 ドロップテーブル mysql_rm_テーブル -->mysql_rm_table_no_locks -->ベーステーブルを削除する -->ha_delete_table -—>ハンドラ::ha_delete_table -->ha_innobase::delete_table -->innobase_basic_ddl::delete_impl -->mysql の行削除テーブル -->Log_DDL::write_drop_log // innodb_dynamic_metadata ログの削除を記録-—>Log_DDL::write_delete_space_log // ibd ログの削除を記録-->dd::drop_table -->dd::cache::Dictionary_client::drop<dd::Table> -->dd::cache::Storage_adapter::drop<dd::Table> -->dd::sdi::drop -->innobase_post_ddl -->Log_DDL::post_ddl -->Log_DDL::replay_by_thread_id -->Log_DDL::再生 —>Log_DDL::replay_delete_space_log // post-ddl は実際に innodb_dynamic_metadata を削除します —>Log_DDL::replay_drop_log // post-ddl は実際に ibd を削除します -->IDで削除 -->DDL_Log_Table::削除 テーブルを削除すると、削除アクション ログのみが記録されます。これらのログはトランザクション全体の一部です。トランザクションが最終的にコミットされると、post_ddl ステージはログを読み取って実際にテーブルを削除します。トランザクションがロールバックされると、ddl_log もトランザクションの一部としてロールバックされます。 参照ドキュメント https://dev.mysql.com/worklog/task/?id=9045 https://dev.mysql.com/worklog/task/?id=9173 https://dev.mysql.com/worklog/task/?id=9175 https://dev.mysql.com/worklog/task/?id=9525 https://dev.mysql.com/worklog/task/?id=9536 要約する 以上は、編集者によるMySQL 8.0 DDLの原子性特性と実装原則の紹介です。皆様のお役に立てれば幸いです。ご質問がございましたら、メッセージを残してください。編集者がすぐに返信いたします。また、123WORDPRESS.COM ウェブサイトをサポートしてくださっている皆様にも感謝申し上げます。 以下もご興味があるかもしれません:
|
<<: React における ref の一般的な使用法の概要
>>: VMware インストール エラー VMware Workstation が VMware 認証サービスを開始できませんでした
MySQL を学習すると、インストール後にいくつかのデフォルトのデータベースが付属していることに気付...
表ラベルの構成HTML 内の表は <table> タグで構成されており、ブラウザはタグを...
1. nginxをダウンロードする [root@localhost my.Shells]# dock...
目次1. 基本2. 問題の説明3. 解決策付録: js を使用して Excel の日付形式を変換する...
<br />関連記事: Web コンテンツ ページ作成に関する 9 つの実用的な提案、W...
requireJS には、baseURL というプロパティがあります。baseURL を設定すること...
1. 古い仮想DOMと新しい仮想DOMを比較し、まずキーが同じかどうかを確認します。 2. 引き続...
目次複数テーブル結合の基本構文クロス結合と直積現象クロスコネクトデカルト積現象内部結合外部結合左外部...
私はパフォーマンス テストを行うために常に Loadrunner を使用してきました。 Loadru...
目次1. CentOS7+MySQL8.0、yumソースインストール2. MySQLにログインしてパ...
NProgress は、ページがジャンプしたときにブラウザの上部に表示される進行状況バーです。公式ウ...
この記事の例では、カウントダウン機能を実装するためのVueの具体的なコードを参考までに共有しています...
前に書いた内容: ビジネス ロジックの判断を行うために、最新のトランザクション ID を表示する必要...
負荷分散の概要Nginx の負荷分散実装を紹介する前に、負荷分散の分類について簡単に説明します。負荷...
1. パーティションテーブルの意味パーティション テーブル定義は、任意のサイズに設定できるルールに従...