メタデータ ロック

このドキュメントでは、TiDB のメタデータ ロックについて紹介します。

概念

TiDB は、オンライン非同期スキーマ変更アルゴリズムを使用して、メタデータ オブジェクトの変更をサポートします。トランザクションが実行されると、トランザクションの開始時に対応するメタデータ スナップショットが取得されます。トランザクション中にメタデータが変更された場合、データの一貫性を確保するために、TiDB はInformation schema is changedエラーを返し、トランザクションはコミットに失敗します。

この問題を解決するために、TiDB v6.3.0 ではオンライン DDL アルゴリズムにメタデータ ロックが導入されています。ほとんどの DML エラーを回避するために、TiDB は、テーブル メタデータの変更中に DML と DDL の優先順位を調整し、実行中の DDL が古いメタデータを持つ DML がコミットされるのを待機させます。

シナリオ

TiDB のメタデータ ロックは、次のようなすべての DDL ステートメントに適用されます。

メタデータ ロックを有効にすると、TiDB での DDL タスクの実行にパフォーマンスに影響を与える可能性があります。影響を軽減するために、メタデータ ロックを必要としないいくつかのシナリオを次に示します。

  • 自動コミットが有効になっているSELECT件のクエリ
  • ステイル読み取りが有効になっています
  • 一時テーブルへのアクセス

使用法

メタデータ ロックを有効にするかどうかを制御するには、システム変数tidb_enable_metadata_lockを使用できます。

原則

バックグラウンド

TiDB での DDL 操作はオンライン DDL モードです。 DDL ステートメントが実行されている場合、変更対象の定義済みオブジェクトのメタデータ バージョンは、複数のマイナー バージョン変更を経る場合があります。オンライン非同期メタデータ変更アルゴリズムは、隣接する 2 つのマイナー バージョンに互換性があることのみを確立します。つまり、2 つのバージョン間の操作によって、DDL が変更するオブジェクトのデータの一貫性が失われることはありません。

テーブルにインデックスを追加すると、DDL ステートメントの状態は次のように変化します: None -> Delete Only、Delete Only -> Write Only、Write Only -> Write Reorg、Write Reorg -> Public。

次のトランザクションのコミット プロセスは、前述の制約に違反しています。

取引トランザクションで使用されるバージョンクラスター内の最新バージョンバージョン違い
txn1なしなし0
txn2削除のみ削除のみ0
txn3書き込み専用書き込み専用0
txn4なし書き込み専用2
txn5書き込み再編成書き込み再編成0
txn6書き込み専用書き込み再編成1
txn7公衆公衆0

前の表では、 txn4がコミットされたときに使用されるメタデータ バージョンは、クラスター内の最新バージョンとは異なる 2 つのバージョンです。これにより、データの不整合が発生する可能性があります。

実装の詳細

メタデータ ロックにより、TiDB クラスター内のすべてのトランザクションで使用されるメタデータ バージョンが最大で 1 つのバージョンだけ異なることを保証できます。この目標を達成するために、TiDB は次の 2 つのルールを実装しています。

  • DML を実行すると、TiDB は、テーブル、ビュー、および対応するメタデータ バージョンなど、トランザクション コンテキストで DML によってアクセスされるメタデータ オブジェクトを記録します。これらのレコードは、トランザクションがコミットされるときにクリーンアップされます。
  • DDL ステートメントの状態が変化すると、メタデータの最新バージョンがすべての TiDB ノードにプッシュされます。 TiDB ノードでのこの状態変更に関連するすべてのトランザクションで使用されるメタデータ バージョンと現在のメタデータ バージョンの差が 2 未満の場合、TiDB ノードはメタデータ オブジェクトのメタデータ ロックを取得すると見なされます。次の状態変更は、クラスター内のすべての TiDB ノードがメタデータ オブジェクトのメタデータ ロックを取得した後にのみ実行できます。

影響

  • DML の場合、メタデータ ロックはその実行をブロックせず、デッドロックも引き起こしません。

  • メタデータ ロックが有効になっている場合、トランザクション内のメタデータ オブジェクトの情報は、最初のアクセスで決定され、その後は変更されません。

  • DDL の場合、メタデータの状態を変更すると、古いトランザクションによって DDL がブロックされることがあります。次に例を示します。

    セッション1セッション 2
    CREATE TABLE t (a INT);
    INSERT INTO t VALUES(1);
    BEGIN;
    ALTER TABLE t ADD COLUMN b INT;
    SELECT * FROM t;
    (テーブルtの現在のメタデータ バージョンを使用します。 (a=1,b=NULL)を返し、テーブルtをロックします。)
    ALTER TABLE t ADD COLUMN c INT; (セッション 1 でブロック)

    反復可能読み取り分離レベルでは、トランザクションの開始からテーブルのメタデータを決定する時点までに、インデックスの追加や列の型の変更など、データの変更を必要とする DDL が実行されると、DDL は次のようにエラーを返します。 :

    セッション1セッション 2
    CREATE TABLE t (a INT);
    INSERT INTO t VALUES(1);
    BEGIN;
    ALTER TABLE t ADD INDEX idx(a);
    SELECT * FROM t; (インデックスidxは使用できません)
    COMMIT;
    BEGIN;
    ALTER TABLE t MODIFY COLUMN a CHAR(10);
    SELECT * FROM t; (エラーInformation schema is changedを返す)

ブロックされた DDL のトラブルシューティング

TiDB v6.3.0 では、現在ブロックされている DDL の情報を取得するのに役立つmysql.tidb_mdl_viewのビューが導入されています。

ノート:

mysql.tidb_mdl_viewのビューを選択するには、 PROCESS権限が必要です。

次の例では、テーブルtのインデックスを追加します。 DDL ステートメントALTER TABLE t ADD INDEX idx(a)があるとします。

SELECT * FROM mysql.tidb_mdl_view\G *************************** 1. row *************************** JOB_ID: 141 DB_NAME: test TABLE_NAME: t QUERY: ALTER TABLE t ADD INDEX idx(a) SESSION ID: 2199023255957 TxnStart: 08-30 16:35:41.313(435643624013955072) SQL_DIGESTS: ["begin","select * from `t`"] 1 row in set (0.02 sec)

前の出力から、 SESSION ID2199023255957のトランザクションがADD INDEXの DDL をブロックしていることがわかります。 SQL_DIGESTは、このトランザクションによって実行される SQL ステートメントを示しています。これは["begin","select * from `t`"]です。ブロックされた DDL を引き続き実行するには、次のグローバルKILLステートメントを使用して2199023255957トランザクションを強制終了します。

mysql> KILL 2199023255957; Query OK, 0 rows affected (0.00 sec)

トランザクションを強制終了した後、 mysql.tidb_mdl_viewのビューを再度選択できます。この時点で、前のトランザクションは出力に表示されません。これは、DDL がブロックされていないことを意味します。

SELECT * FROM mysql.tidb_mdl_view\G Empty set (0.01 sec)