## モジュール 5: ノンブロッキング・オンライン DDL でスキーマ進化

![demo_203-tidb-x-online-ddl-ja.jpg 85%](https://lab-static.pingcap.com/images/2025/12/2/532f71ad4c371cf3a4a87f408ae460648d852342.jpg)

TiDB Cloud では、システム変数 `tidb_enable_metadata_lock` を `OFF` に設定することで、DMLトランザクションロックによるDDL操作のブロックを防ぎ、真のノンブロッキング・オンラインDDLを実現できます。

最大の利点は、DDL操作が非同期で行われるため、同じテーブルに対してDMLとDDLを同時に実行でき、互いをブロックしません。これにより、TiDBではテーブルスキーマの進化が非常に容易です。一般的に、アプリケーションコードが新旧両方のスキーマに事前対応されていれば、DBAは計画的な業務停止なしにDDL操作でテーブル構造を変更できます。

### 手順 1. 2つのデータベース接続を確立する

- [ターミナル 2](ssh://monitor1-ssh#2) をクリックして、もう1つのターミナルを開きます。

- [ターミナル 1](ssh://monitor1-ssh#1) で、まだ接続していなければTiDBに接続してください。

  ```bash
  mysql \
   --comments -u '${LAB:SERVERLESS_CLUSTER_USERNAME}' -p'${LAB:SERVERLESS_CLUSTER_PASSWORD}' \
   -h ${LAB:SERVERLESS_CLUSTER_HOST} -P ${LAB:SERVERLESS_CLUSTER_PORT} \
   --ssl-mode=VERIFY_IDENTITY --ssl-ca=/etc/ssl/certs/ca-certificates.crt \
   -D ${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}
  ```

- [ターミナル 2](ssh://monitor1-ssh#2) でも同様にデータベースへ接続します。

  ```bash
  mysql \
   --comments -u '${LAB:SERVERLESS_CLUSTER_USERNAME}' -p'${LAB:SERVERLESS_CLUSTER_PASSWORD}' \
   -h ${LAB:SERVERLESS_CLUSTER_HOST} -P ${LAB:SERVERLESS_CLUSTER_PORT} \
   --ssl-mode=VERIFY_IDENTITY --ssl-ca=/etc/ssl/certs/ca-certificates.crt \
   -D ${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}
  ```

### 手順 2. メタデータロックのデフォルト動作を理解する

- [ターミナル 1](ssh://monitor1-ssh#1) および [ターミナル2](ssh://monitor1-ssh#2) の双方で、メタデータロックがデフォルトで有効 (ON) になっていることを確認します。

  ```sql
  SHOW SESSION VARIABLES LIKE 'tidb_enable_metadata_lock';
  ```

  ```output
  +---------------------------+-------+
  | Variable_name             | Value |
  +---------------------------+-------+
  | tidb_enable_metadata_lock | ON    |
  +---------------------------+-------+
  1 row in set (0.01 sec)
  ```

- ここでは、カラム追加（スキーマ変更）がどう動作するかを体験してみましょう。

- [ターミナル 1](ssh://monitor1-ssh#1) で、以下のトランザクション制御とDMLコマンドを実行して本のレコードを更新します。この時、行レベルロックに加えてテーブルのメタデータロックも有効です。トランザクションが完了しない限り、両方のロックが維持されます。

  ```sql
  BEGIN;
  UPDATE books SET stock = stock+1 WHERE id = 1;
  ```

  ```output
  Query OK, 1 row affected (0.01 sec)
  Rows matched: 1  Changed: 1  Warnings: 0
  ```

- [ターミナル 2](ssh://monitor1-ssh#2) で、同じテーブルにカラムを追加する以下のDDLを実行します。このDDL操作は、もう一方のセッションでDMLトランザクションが継続中のため、**ブロック** されます。

  ```sql
  ALTER TABLE books ADD COLUMN (valid_for_training INT);
  ```

- 再び [ターミナル 1](ssh://monitor1-ssh#1) へ戻り、トランザクションを終了させてメタデータロックを解放しましょう。

  ```sql
  COMMIT;
  ```

  ```output
  Query OK, 0 rows affected (0.02 sec)
  ```

- [ターミナル 2](ssh://monitor1-ssh#2) に戻ると、DDL操作はトランザクションロックが解放された直後に完了することがわかります。したがって、実施に数十秒〜数分の遅延（例：1分37.93秒）が発生します。この待ち時間は、トランザクション終了までの実際の操作に依存します。

### 手順 3. ノンブロッキング・オンライン DDL を有効化

- [ターミナル 1](ssh://monitor1-ssh#1) で、以下のコマンドを実行しメタデータロックを無効 (OFF) にします。

  ```sql
  SET GLOBAL tidb_enable_metadata_lock = OFF;
  ```

- [ターミナル 1](ssh://monitor1-ssh#1) と [ターミナル2](ssh://monitor1-ssh#2) の両方で、メタデータロックがOFFになっていることを確認します。

  ```sql
  SHOW SESSION VARIABLES LIKE 'tidb_enable_metadata_lock';
  ```

  ```output
  +---------------------------+-------+
  | Variable_name             | Value |
  +---------------------------+-------+
  | tidb_enable_metadata_lock | OFF   |
  +---------------------------+-------+
  1 row in set (0.01 sec)
  ```

- では再度、カラム追加のスキーマ変更シナリオを検証します。今回はDMLがDDLをブロックしません。

- [ターミナル 1](ssh://monitor1-ssh#1) で、次のトランザクション制御とDMLコマンドを実行します。

  ```sql
  BEGIN;
  UPDATE books SET stock = stock+2 WHERE id = 1;
  ```

  ```output
  Query OK, 1 row affected (0.01 sec)
  Rows matched: 1  Changed: 1  Warnings: 0
  ```

- [ターミナル 2](ssh://monitor1-ssh#2) で、同じテーブルにカラムを追加するDDLを再度実行します。今度は進行中のDML操作に関係なく、DDLが即座に実行されブロックされません。これがノンブロッキング・オンラインDDLの効果です。

  ```sql
  ALTER TABLE books ADD COLUMN (isbn INT);
  ```

  ```output
  Query OK, 0 rows affected (0.65 sec)
  ```

- [ターミナル 1](ssh://monitor1-ssh#1) に戻り、トランザクションのコミットを試みます。ただし、トランザクションの開始後に `books` テーブルのスキーマが変更されているため、コミットは失敗し、トランザクションはロールバックされます。これもノンブロッキングDDLの特徴です。

  ```sql
  COMMIT;
  ```

  ```output
  ERROR 8028 (HY000): Information schema is changed during the execution of the statement(for example, table definition may be updated by other DDL ran in parallel). If you see this error often, try increasing `tidb_max_delta_schema_count`. [try again later]
  ```

- この時点で疑問が湧くかもしれません：「ノンブロッキング・オンラインDDLを有効にすると、進行中のDMLが `ERROR 8028` で失敗しロールバックされるのはなぜ良いことなのか？」。その鍵は、DDLコマンドが一般に計画的メンテナンスイベントであることにあります。例えば、`books` テーブルへ業務要件として `isbn` カラムを追加する場合、アプリケーション側で新カラムに対応し、`ERROR 8028` 発生時はリトライ処理すれば済みます。この仕組みの大きな利点は、DDL操作実行中もビジネスアプリケーション（DML操作）は停止せず並行稼働できる点です。つまり、TiDBのノンブロッキングDDLとCI/CD対応アプリケーションを組み合わせることで、完全オンラインなスキーマ変更・ゼロダウンタイムが実現可能です。

- 具体的に `ERROR 8028` やオンラインDDLの扱いをサンプル実装で解説したハンズオンラボも用意しています。（例: [mysql-connector-python で TiDB Cloud を活用する モジュール5](/labs/demo_405)、[JDBC で TiDB Cloud を活用する モジュール5](/labs/demo_701)）

まとめると、`tidb_enable_metadata_lock` システム変数を有効化・無効化するかはアプリの要件とワークロード特性によって使い分ける必要があります：

  | `tidb_enable_metadata_lock`  | 利点                                   | 欠点                             |
  | :--------------------------- | :-------------------------------------- | :------------------------------- |
  | ON (デフォルト)                | DMLはスキーマ変更の影響を受けない      | DMLがDDLをブロックしうる         |
  | OFF                          | DMLがDDLをブロックしない               | DMLはスキーマ変化に対応要        |

- 設定をデフォルトに戻しておきましょう。

  ```sql
  SET GLOBAL tidb_enable_metadata_lock = ON;
  ```

- それでは、次のモジュールに進みましょう。
