DDL ステートメントの実行原則とベスト プラクティス
このドキュメントでは、TiDB の DDL ステートメントに関連する実行原則とベスト プラクティスの概要を説明します。原則には、DDL 所有者モジュールとオンライン DDL 変更プロセスが含まれます。
DDL実行の原則
TiDB は、オンラインおよび非同期アプローチを使用して DDL ステートメントを実行します。つまり、DDL ステートメントの実行中は、他のセッションの DML ステートメントはブロックされません。つまり、アプリケーションの実行中に、オンラインおよび非同期の DDL ステートメントを使用してデータベース オブジェクトの定義を変更できます。
DDL文の種類
DDL ステートメントが実行中にユーザー アプリケーションをブロックするかどうかに基づいて、DDL ステートメントは次のタイプに分類できます。
オフライン DDL ステートメント: データベースは、ユーザーから DDL ステートメントを受け取ると、まず変更するデータベース オブジェクトをロックし、次にメタデータを変更します。DDL の実行中、データベースはユーザー アプリケーションによるデータの変更をブロックします。
オンライン DDL ステートメント: データベースで DDL ステートメントが実行される場合、ステートメントがユーザー アプリケーションをブロックしないようにするために、特定のメソッドが使用されます。これにより、ユーザーは 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 ノード、または
ADD INDEX
タスクを実行するために TiDB Distributed eXecution Framework (DXF) によってスケジュールされた TiDB ノードが、対応する計算を実行するために TiDB の CPU リソースを消費することです。注記:
通常、物理 DDL タスクの実行は、ユーザー アプリケーションに最も大きな影響を及ぼします。したがって、この影響を最小限に抑えるには、実行中に物理 DDL ステートメントの設計を最適化することが重要です。これにより、ユーザー アプリケーションへの影響を軽減できます。
TiDB DDL モジュール
TiDB DDL モジュールは、TiDB クラスター内のすべての DDL ステートメントを実行するプロキシとして機能する DDL 所有者 (またはオーナー) の役割を導入します。現在の実装では、クラスター全体で一度に 1 つの TiDB ノードのみを所有者として選出できます。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 ステートメントの実行中、変更オブジェクトの小さなバージョン間の差が 2 バージョンを超えない限り、同じクラスター内の TiDB ノードは異なる小さなバージョン変更を持つことができます。これは、隣接する小さなバージョンが相互に互換性があるため可能です。
このように、複数の小さなバージョンを進化させることで、メタデータを複数の TiDB ノード間で正しく同期できるようになります。これにより、プロセス中にデータを変更するユーザー トランザクションの正確性と一貫性が維持されます。
ADD INDEX
例にとると、状態変化の全体のプロセスは次のようになります。
absent -> delete only -> write only -> write reorg -> public
ユーザーにとって、新しく作成されたインデックスはpublic
状態より前は使用できません。
- Online DDL asynchronous change before TiDB v6.2.0
- Parallel DDL framework starting from v6.2.0
v6.2.0 より前では、 TiDB SQLレイヤーで非同期スキーマ変更を処理するプロセスは次のとおりです。
MySQL クライアントは TiDBサーバーに DDL 要求を送信します。
TiDBサーバーはリクエストを受信すると、MySQL プロトコルレイヤーでリクエストを解析して最適化し、実行のためにTiDB SQLレイヤーに送信します。
TiDB の SQLレイヤーは、DDL 要求を受信すると、
start job
モジュールを起動して要求を特定の DDL ジョブ (つまり、DDL タスク) にカプセル化し、このジョブをステートメントの種類に基づいて KVレイヤーの対応する 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 実行を参照してください。
TiDB v6.2.0 より前では、所有者は一度に同じタイプ (論理または物理) の DDL タスクを 1 つしか実行できないため、比較的厳格であり、ユーザー エクスペリエンスに影響します。
DDL タスク間に依存関係がない場合、並列実行はデータの正確性と一貫性に影響を与えません。たとえば、ユーザー A がT1
のテーブルにインデックスを追加し、ユーザー B がT2
のテーブルから列を削除するとします。これらの 2 つの DDL ステートメントは並列で実行できます。
DDL 実行のユーザー エクスペリエンスを向上させるために、TiDB v6.2.0 以降では、所有者が DDL タスクの関連性を判断できるようになりました。ロジックは次のとおりです。
- 同じテーブルに対して実行される DDL ステートメントは相互にブロックされます。
DROP DATABASE
およびデータベース内のすべてのオブジェクトに影響する DDL ステートメントは相互にブロックされます。- 異なるテーブルでのインデックスの追加と列タイプの変更を同時に実行できます。
- 論理 DDL ステートメントは、前の論理 DDL ステートメントが実行されるまで待機してから実行する必要があります。
- その他の場合、同時 DDL 実行の可用性レベルに基づいて DDL を実行できます。
具体的には、TiDB は v6.2.0 で DDL 実行フレームワークを次の点でアップグレードしました。
DDL 所有者は、前述のロジックに基づいて DDL タスクを並列に実行できます。
DDL ジョブ キューの先入先出の問題が解決されました。DDL 所有者はキューの最初のジョブを選択するのではなく、現時点で実行できるジョブを選択します。
物理 DDL ステートメントを処理するワーカーの数が増加し、複数の物理 DDL ステートメントを並列に実行できるようになりました。
TiDB のすべての DDL タスクはオンライン変更アプローチを使用して実装されるため、TiDB は所有者を通じて新しい DDL ジョブの関連性を判断し、この情報に基づいて DDL タスクをスケジュールできます。このアプローチにより、分散データベースは従来のデータベースと同じレベルの DDL 同時実行性を実現できます。
同時実行 DDL フレームワークにより、TiDB の 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 操作を実行する適切な時間を選択します。たとえば、アプリケーションの負荷が低いときに
ADD INDEX
操作を実行することをお勧めします。- インデックスの追加にかかる時間は比較的長いため、TiDB はコマンドの送信後にバックグラウンドでタスクを実行します。TiDBサーバーがダウンしても、実行には影響しません。
DDLリクエストを同時に送信して多数のテーブルを素早く作成
テーブル作成操作には約 50 ミリ秒かかります。フレームワークの制限により、テーブルの作成にかかる実際の時間は長くなる場合があります。
テーブルをより速く作成するには、複数の DDL リクエストを同時に送信して、テーブル作成速度を最速にすることをお勧めします。DDL リクエストをシリアルで送信し、オーナー ノードに送信しないと、テーブル作成速度が非常に遅くなります。
1つのALTER
文で複数の変更を行う
v6.2.0 以降、TiDB は、ステートメント全体のアトミック性を確保しながら、単一のALTER
ステートメントでテーブルの複数のスキーマ オブジェクト (列やインデックスなど) を変更することをサポートしています。したがって、単一のALTER
ステートメントで複数の変更を行うことをお勧めします。
読み取りと書き込みのパフォーマンスを確認する
TiDB がインデックスを追加する場合、データのバックフィルのフェーズwrite reorg
ADD INDEX
が開始したら、Grafana ダッシュボードで TiDB と TiKV の読み取りと書き込みのパフォーマンス メトリックとアプリケーションの応答時間をチェックして、 ADD INDEX
操作がクラスターに影響するかどうかを判断することをお勧めします。
DDL関連コマンド
ADMIN SHOW DDL
: 現在のスキーマ バージョン番号、DDL 所有者の DDL ID とアドレス、実行中の DDL タスクと SQL、現在の TiDB インスタンスの DDL ID など、TiDB DDL 操作のステータスを表示するために使用されます。詳細については、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 実行に関するよくある質問については、 SQL FAQ - DDL 実行参照してください。