TiDB での DDL 実行のベストプラクティス
このドキュメントでは、TiDBにおけるDDLステートメントの実行原則とベストプラクティスの概要を説明します。これらの原則には、DDLオーナーモジュールとオンラインDDL変更プロセスが含まれます。
DDL実行原則
TiDBは、DDL文の実行にオンラインかつ非同期のアプローチを採用しています。つまり、DDL文の実行中は、他のセッションのDML文がブロックされることはありません。つまり、アプリケーションの実行中に、オンラインおよび非同期のDDL文を使用してデータベースオブジェクトの定義を変更できます。
DDL文の種類
TiDBはオンラインDDLをサポートしています。つまり、データベースでDDL文が実行される際、特定の方法を用いて、文がユーザーアプリケーションをブロックしないようにします。DDL実行中にデータ変更を送信することができ、データベースはデータの一貫性と正確性を保証します。
一方、オフラインDDLはデータベースオブジェクトをロックし、DDL操作が完了するまでユーザーによる変更をブロックします。TiDBはオフラインDDLをサポートしていません。
DDL 文は、対象となる DDL オブジェクトに含まれるデータを操作するかどうかによって、次の種類に分類されます。
論理 DDL ステートメント: 論理 DDL ステートメントは通常、テーブル名の変更や列名の変更など、オブジェクトに格納されているデータを処理せずに、データベース オブジェクトのメタデータのみを変更します。
TiDBでは、論理DDL文は「一般DDL」とも呼ばれます。これらの文は通常、実行時間が短く、完了までに数十ミリ秒または数秒しかかからないことがよくあります。そのため、システムリソースをあまり消費せず、アプリケーションのワークロードにも影響を与えません。
物理DDL文:物理DDL文は、変更対象となるオブジェクトのメタデータを変更するだけでなく、オブジェクトに格納されているユーザーデータも変更します。例えば、TiDBがテーブルのインデックスを作成する場合、テーブルの定義を変更するだけでなく、新しく追加されたインデックスを構築するためにテーブル全体のスキャンを実行します。
TiDBでは、物理DDL文は「reorg DDL」(再編成)とも呼ばれます。現在、物理DDL文には、
ADD INDEX
と非可逆的な列型変更(例えば、INT
型からCHAR
型への変更)のみが含まれます。これらの文の実行には長い時間がかかり、実行時間はテーブル内のデータ量、マシン構成、アプリケーションのワークロードによって影響を受けます。物理DDL文の実行は、2つの理由からアプリケーションのワークロードに影響を与える可能性があります。1つは、データの読み取りと書き込みにTiKVのCPUリソースとI/Oリソースを消費することです。もう1つは、 DDLオーナーとして機能するTiDBノード、またはTiDB Distributed eXecution Framework (DXF)によって
ADD INDEX
タスクを実行するようにスケジュールされたTiDBノードが、対応する計算を実行するためにTiDBのCPUリソースを消費することです。注記:
物理DDLタスクの実行は、通常、ユーザーアプリケーションに最も大きな影響を与えます。したがって、この影響を最小限に抑えるには、実行時に物理DDL文の設計を最適化することが重要です。これにより、ユーザーアプリケーションへの影響を軽減できます。
TiDB DDLモジュール
TiDB DDLモジュールは、DDLオーナー(またはオーナー)の役割を導入します。これは、TiDBクラスタ内のすべてのDDL文を実行するためのプロキシとして機能します。現在の実装では、クラスタ全体から一度にオーナーとして選出できるTiDBノードは1つだけです。TiDBノードがオーナーとして選出されると、そのTiDBノードで起動されたワーカーがクラスタ内のDDLタスクを処理できるようになります。
TiDBは、etcdの選出メカニズムを使用して、複数のTiDBノードからオーナーをホストするノードを選出します。デフォルトでは、各TiDBノードがオーナーとして選出される可能性があります(選出へのノードの参加を管理するには、 run-ddl
設定します)。選出されたオーナーノードには任期があり、更新することで積極的に任期を維持します。オーナーノードがダウンした場合、etcdを介して別のノードが新しいオーナーとして選出され、クラスター内でDDLタスクの実行を継続できます。
DDL 所有者の簡単な説明は次のとおりです。
ADMIN SHOW DDL
ステートメントを使用して、現在の DDL 所有者を表示できます。
ADMIN SHOW DDL;
+------------+--------------------------------------+---------------+--------------+--------------------------------------+-------+
| SCHEMA_VER | OWNER_ID | OWNER_ADDRESS | RUNNING_JOBS | SELF_ID | QUERY |
+------------+--------------------------------------+---------------+--------------+--------------------------------------+-------+
| 26 | 2d1982af-fa63-43ad-a3d5-73710683cc63 | 0.0.0.0:4000 | | 2d1982af-fa63-43ad-a3d5-73710683cc63 | |
+------------+--------------------------------------+---------------+--------------+--------------------------------------+-------+
1 row in set (0.00 sec)
TiDBにおけるオンラインDDL非同期変更の仕組み
TiDB DDL モジュールは設計当初からオンライン非同期変更モードを選択しており、これによりダウンタイムを経験することなくアプリケーションを変更できます。
DDL変更は、ある状態から別の状態への遷移を伴い、通常は「変更前」の状態から「変更後」の状態へと遷移します。オンラインDDL変更では、この遷移は、相互に互換性のある複数の小さなバージョン状態を導入することで発生します。DDL文の実行中、同じクラスター内のTiDBノードは、変更オブジェクトの小さなバージョン間の差異が2バージョン以内であれば、異なる小さなバージョン変更を持つことができます。これは、隣接する小さなバージョンが相互に互換性を持つことができるためです。
このように、複数の小さなバージョンを経ることで、複数のTiDBノード間でメタデータが正しく同期されます。これにより、プロセス中にデータの変更を伴うユーザートランザクションの正確性と一貫性が維持されます。
ADD INDEX
例にとると、状態変化の全体プロセスは次のようになります。
absent -> delete only -> write only -> write reorg -> public
ユーザーにとって、新しく作成されたインデックスはpublic
状態より前は使用できません。
TiDB v6.2.0 より前では、所有者は一度に同じタイプ (論理または物理) の DDL タスクを 1 つしか実行できないため、制限が比較的厳しく、ユーザー エクスペリエンスに影響します。
DDLタスク間に依存関係がない場合、並列実行はデータの正確性と一貫性に影響を与えません。例えば、ユーザーAがテーブルT1
にインデックスを追加し、ユーザーBがテーブルT2
から列を削除するとします。これらの2つのDDL文は並列実行可能です。
DDL実行のユーザーエクスペリエンスを向上させるため、TiDB v6.2.0以降では、オーナーがDDLタスクの関連性を判断できるようになりました。そのロジックは次のとおりです。
- 同じテーブルに対して実行される DDL ステートメントは相互にブロックされます。
DROP DATABASE
と、データベース内のすべてのオブジェクトに影響する DDL ステートメントは相互にブロックされます。- 異なるテーブルでのインデックスの追加と列タイプの変更を同時に実行できます。
- v8.2.0 以降では、異なるテーブルに対して論理DDL文並列実行できます。
- それ以外の場合、同時 DDL 実行の可用性レベルに基づいて DDL を実行できます。
具体的には、TiDB 6.2.0 では、次の点で DDL 実行フレームワークが強化されました。
DDL 所有者は、前述のロジックに基づいて DDL タスクを並列に実行できます。
DDLジョブキューにおける先入先出法の問題が解決されました。DDLオーナーはキュー内の最初のジョブを選択するのではなく、現時点で実行可能なジョブを選択するようになりました。
物理 DDL 文を処理するワーカーの数が増加し、複数の物理 DDL 文を並列に実行できるようになりました。
TiDBのすべてのDDLタスクはオンライン変更アプローチを使用して実装されているため、TiDBはオーナーを通じて新しいDDLジョブの関連性を判断し、その情報に基づいてDDLタスクをスケジュールすることができます。このアプローチにより、分散データベースは従来のデータベースと同等のDDL同時実行性を実現できます。
同時実行 DDL フレームワークにより、TiDB の DDL ステートメントの実行機能が強化され、商用データベースの使用パターンとの互換性が向上します。
v6.2.0 より前では、 TiDB SQLレイヤーで非同期スキーマ変更を処理するプロセスは次のとおりです。
MySQL クライアントは TiDBサーバーに DDL 要求を送信します。
リクエストを受信すると、TiDBサーバーはMySQL プロトコルレイヤーでリクエストを解析および最適化し、実行のためにTiDB SQLレイヤーに送信します。
TiDBのSQLレイヤーはDDLリクエストを受信すると、
start job
モジュールを起動してリクエストを特定のDDLジョブ(つまりDDLタスク)にカプセル化し、ステートメントの種類に基づいてKVレイヤーの対応するDDLジョブキューに格納します。処理が必要なジョブは、対応するワーカーに通知されます。ジョブ処理の通知を受け取ると、ワーカーはDDLオーナーのロールを持っているかどうかを判断します。DDLオーナーのロールを持っている場合は、ジョブを直接処理します。そうでない場合は、何も処理せずに終了します。
TiDBサーバーがオーナーロールでない場合は、別のノードがオーナーになる必要があります。オーナーロールのノードのワーカーは、実行可能なジョブがあるかどうかを定期的に確認します。実行可能なジョブが見つかった場合、ワーカーはそのジョブを処理します。
ワーカーはジョブを処理した後、KVレイヤーのジョブキューからジョブを削除し、
job history queue
に配置します。ジョブをカプセル化されたstart job
モジュールは、job history queue
内のジョブのIDを定期的にチェックし、処理済みかどうかを確認します。処理済みであれば、ジョブに対応するDDL操作全体が終了します。TiDBサーバーは、DDL 処理結果を MySQL クライアントに返します。
TiDB v6.2.0 より前では、DDL 実行フレームワークには次の制限がありました。
- TiKV クラスターには、それぞれ論理 DDL と物理 DDL を処理するキュー
general job queue
とadd index job queue
2 つのキューのみがあります。 - DDL 所有者は常に先入先出方式で DDL ジョブを処理します。
- DDL 所有者は、一度に同じタイプ (論理または物理) の DDL タスクを 1 つだけ実行できます。これは比較的厳格であり、ユーザー エクスペリエンスに影響します。
これらの制限により、意図しないDDLブロッキング動作が発生する可能性があります。詳細については、 SQL FAQ - DDL実行参照してください。
ベストプラクティス
システム変数を通じて物理的なDDL実行速度とアプリケーション負荷への影響をバランスさせる
物理 DDL 文 (インデックスの追加や列タイプの変更を含む) を実行する場合、次のシステム変数の値を調整して、DDL 実行の速度とアプリケーションの負荷への影響のバランスを取ることができます。
tidb_ddl_reorg_worker_cnt
: この変数は、バックフィルの同時実行を制御する DDL 操作の再編成ワーカーの数を設定します。tidb_ddl_reorg_batch_size
: この変数は、第re-organize
フェーズの DDL 操作のバッチ サイズを設定し、バックフィルされるデータの量を制御します。推奨値:
- 他に負荷がない場合は、
tidb_ddl_reorg_worker_cnt
とtidb_ddl_reorg_batch_size
値を増やすことでADD INDEX
処理を高速化できます。例えば、2つの変数の値をそれぞれ20
と2048
に設定することができます。 - 他の負荷がある場合は、
tidb_ddl_reorg_worker_cnt
とtidb_ddl_reorg_batch_size
の値を減らして、他のアプリケーションへの影響を最小限に抑えることができます。例えば、これらの変数の値をそれぞれ4
と256
に設定することができます。
- 他に負荷がない場合は、
ヒント:
- 上記の 2 つの変数は、DDL タスクの実行中に動的に調整され、次のトランザクション バッチで有効になります。
- DDL操作の種類とアプリケーションの負荷状況に応じて、適切なタイミングでDDL操作を実行してください。例えば、アプリケーションの負荷が低い場合は、
ADD INDEX
操作を実行することをお勧めします。- インデックスの追加には比較的長い時間がかかるため、TiDBはコマンド送信後、バックグラウンドでタスクを実行します。TiDBサーバーがダウンしていても、実行には影響しません。
DDLリクエストを同時に送信して多数のテーブルを素早く作成する
テーブル作成処理には約50ミリ秒かかります。フレームワークの制限により、実際のテーブル作成時間はこれより長くなる場合があります。
テーブル作成速度を高速化するには、複数のDDLリクエストを同時に送信することをお勧めします。これにより、テーブル作成速度を最大化できます。DDLリクエストをシリアル送信し、Ownerノードに送信しない場合、テーブル作成速度は非常に遅くなります。
1つのALTER
文で複数の変更を行う
TiDB v6.2.0以降、単一のALTER
文でテーブル内の複数のスキーマオブジェクト(列やインデックスなど)を変更できるようになりました。ただし、文全体のアトミック性は維持されます。そのため、複数の変更は単一のALTER
文で行うことをお勧めします。
読み取りと書き込みのパフォーマンスを確認する
TiDBがインデックスを追加する際、データのバックフィルフェーズによってクラスターの読み取りと書き込みに負荷がかかります。1 ADD INDEX
のコマンドが送信され、 write reorg
番目のフェーズが開始されたら、GrafanaダッシュボードでTiDBとTiKVの読み取りと書き込みのパフォーマンスメトリックとアプリケーションの応答時間を確認し、 ADD INDEX
操作がクラスターに影響を与えているかどうかを確認することをお勧めします。
DDL関連コマンド
ADMIN SHOW DDL
: TiDB DDL操作のステータス(現在のスキーマバージョン番号、DDL所有者のDDL IDとアドレス、実行中のDDLタスクとSQL、現在のTiDBインスタンスのDDL IDなど)を表示するために使用されます。詳細については、ADMIN SHOW DDL
参照してください。ADMIN SHOW DDL JOBS
: クラスター環境で実行されているDDLタスクの詳細なステータスを表示するために使用されます。詳細については、ADMIN SHOW DDL JOBS
参照してください。ADMIN SHOW DDL JOB QUERIES job_id [, job_id]
:job_id
に対応するDDLタスクの元のSQL文を表示するために使用されます。詳細はADMIN SHOW DDL JOB QUERIES
参照してください。ADMIN CANCEL DDL JOBS job_id, [, job_id]
: 送信されたが完了していないDDLタスクをキャンセルするために使用されます。キャンセルが完了すると、DDLタスクを実行するSQL文はエラーERROR 8214 (HY000): Cancelled DDL job
を返します。完了した DDL タスクがキャンセルされた場合、
RESULT
列にDDL Job:90 not found
エラーが表示されます。これは、タスクが DDL 待機キューから削除されたことを意味します。ADMIN PAUSE DDL JOBS job_id [, job_id]
: 実行中のDDLジョブを一時停止します。コマンド実行後、バックグラウンドジョブが一時停止されている間、DDLジョブを実行するSQL文は実行中として表示されます。詳細はADMIN PAUSE DDL JOBS
を参照してください。一時停止できるのは、進行中またはキュー内にあるDDLタスクのみです。それ以外の場合は、列
RESULT
にエラーJob 3 can't be paused now
が表示されます。ADMIN RESUME DDL JOBS job_id [, job_id]
: 一時停止中のDDLタスクを再開します。コマンド実行後、DDLタスクを実行するSQL文が実行中として表示され、バックグラウンドタスクが再開されます。詳細はADMIN RESUME DDL JOBS
を参照してください。一時停止中のDDLタスクのみを再開できます。それ以外の場合は、
RESULT
列目にエラーJob 3 can't be resumed
が表示されます。
DDL関連テーブル
information_schema.DDL_JOBS
: 現在実行中および終了した DDL ジョブに関する情報。mysql.tidb_mdl_view
: メタデータロックビューに関する情報。DDLの進行を妨げているクエリを特定するのに役立ちます。
よくある質問
DDL 実行に関するよくある質問については、 SQL FAQ - DDL 実行参照してください。