## モジュール 4: オンラインスキーマ変更

オンラインスキーマ変更は、中断のないアップタイムと継続的な可用性がシームレスなユーザーエクスペリエンスを確保するために重要な SaaS 環境では特に重要です。このモジュールでは、同じテーブルで同時に DML 挿入が発生している間に、ビジネス変更をサポートするために既存のテーブルに新しい列を追加する DDL 操作を実行します。DDL 操作が進行中の DML によってブロックされないことを観察し、DML を実行している Java アプリケーションが TiDB のオンライン DDL 機能を認識するように設計されていることを確認します。スキーマ変更を期待し、シームレスに処理するようにプログラムされています。

### 手順 1. sysbench によって作成されたスキーマを確認する

+ [ターミナル 1](ssh://monitor1-ssh#1) で、TiDB サーバーインスタンスに接続します。

  ```bash
  mysql --prompt="mysql [\d] > " -h ${HOST_DB1_PRIVATE_IP} -P 4000 -u root
  ```

  ```output
  mysql [(none)] >
  ```

+ すべての行を削除し、テーブル `sbtest1000.sbtest9` の定義を確認します。テーブルには4つのカラムが定義されており、`id` が主キーです。

  ```sql
  DELETE FROM sbtest1000.sbtest9;
  DESC sbtest1000.sbtest9;
  ```

  ```output
  +-------+-----------+------+------+---------+----------------+
  | Field | Type      | Null | Key  | Default | Extra          |
  +-------+-----------+------+------+---------+----------------+
  | id    | int       | NO   | PRI  | NULL    | auto_increment |
  | k     | int       | NO   | MUL  | 0       |                |
  | c     | char(120) | NO   |      |         |                |
  | pad   | char(60)  | NO   |      |         |                |
  +-------+-----------+------+------+---------+----------------+
  4 rows in set (0.00 sec)
  ```

### 手順 2. オンラインスキーマ変更の準備

+ [メタデータロック](https://docs.pingcap.com/tidb/stable/system-variable-reference/#tidb_enable_metadata_lock)をオフにして、DDL ステートメントが同じテーブルで進行中の DML トランザクションによってブロックされないようにします。

  ```sql
  SET GLOBAL tidb_enable_metadata_lock = OFF;
  SHOW VARIABLES LIKE "tidb_enable_metadata_lock";
  EXIT
  ```

+ 提供されたミニ Java プログラムを確認します。これは単純な挿入操作を無限に実行します。そして最も重要なことは、エラー `8028` を期待し、トランザクションを再試行できることです。つまり、TiDB のオンラインスキーマ変更動作と互換性があります。エラーコード `8028`（メッセージ「Information schema is changed」）は、プログラムがトランザクションをコミットしようとしたときに、トランザクション開始時とは異なるスキーマ定義を検出した場合に発生します。

  ```bash
  cat InsertJob.java | egrep -A1 -B1 8028
  ```

  ```output
          } catch (SQLException e) {
              if (e.getErrorCode() == 8028) {
                  System.out.println("Schema mutation encountered, retry ...");
  ```

### 手順 3. 挿入ワークロードを開始する

+ [ターミナル 1](ssh://monitor1-ssh#1) で、以下のコマンドを実行して挿入ワークロードを開始します。これは停止しません。実行し続けてください。次のステップに進んでください。

  ```bash
  rm -f InsertJob.class
  javac -cp .:mysql-connector-java-5.1.36-bin.jar InsertJob.java
  sleep 1
  java -cp .:mysql-connector-java-5.1.36-bin.jar InsertJob ${HOST_DB1_PRIVATE_IP} 4000 t1000
  ```

  ```output
  Inserting 0
  Inserting 1
  Inserting 2
  Inserting 3
  Inserting 4
  Inserting 5
  Inserting 6
  Inserting 7
  ...
  ```

### 手順 4. オンラインスキーマ変更を実行する

+ 特定のビジネス要件に基づいて、テーブル `sbtest1000.sbtest9` に新しい列 `ed VARCHAR(10) DEFAULT 'N/A'` を追加することを決定します。

+ [ターミナル 2](ssh://monitor1-ssh#2) に切り替えて、TiDB サーバーインスタンスに接続します。

  ```bash
  mysql --prompt="mysql [\d] > " -h ${HOST_DB1_PRIVATE_IP} -P 4000 -u root
  ```

  ```output
  mysql [(none)] >
  ```

+ テーブルを 2 回確認し、挿入ワークロードが動作していて行数が増加していることを確認します。

  ```sql
  SELECT COUNT(1) FROM sbtest1000.sbtest9;
  SELECT SLEEP(2);
  SELECT COUNT(1) FROM sbtest1000.sbtest9;
  ```

  ```output
  +----------+
  | COUNT(1) |
  +----------+
  |       14 |
  +----------+
  1 row in set (0.00 sec)

  +----------+
  | SLEEP(2) |
  +----------+
  |        0 |
  +----------+
  1 row in set (2.00 sec)

  +----------+
  | COUNT(1) |
  +----------+
  |       18 |
  +----------+
  1 row in set (0.00 sec)
  ```

+ `sbtest1000.sbtest9` に新しい列を追加する DDL を実行します。

  ```sql
  ALTER TABLE sbtest1000.sbtest9 ADD COLUMN (ed VARCHAR(10) DEFAULT 'N/A');
  ```

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

+ [ターミナル 1](ssh://monitor1-ssh#1) に戻って出力を観察します。`Schema mutation encountered, retry ...` という出力が期待され、ワークロードが中断されないことを確認します。

  ```output
  ...
  Inserting 50
  Inserting 51
  Schema mutation encountered, retry ...
  Inserting 52
  Inserting 53
  Inserting 54
  Inserting 55
  Inserting 56
  ...
  ```

+ [ターミナル 2](ssh://monitor1-ssh#2) に戻って、行数がまだ増加していることを確認します。

  ```sql
  SELECT COUNT(1) FROM sbtest1000.sbtest9;
  SELECT SLEEP(2);
  SELECT COUNT(1) FROM sbtest1000.sbtest9;
  ```

  ```output
  +----------+
  | COUNT(1) |
  +----------+
  |       71 |
  +----------+
  1 row in set (0.01 sec)

  +----------+
  | SLEEP(2) |
  +----------+
  |        0 |
  +----------+
  1 row in set (2.00 sec)

  +----------+
  | COUNT(1) |
  +----------+
  |       74 |
  +----------+
  1 row in set (0.00 sec)
  ```

+ テーブル定義が変更されたことを確認します。列 `ed varchar(10)` が新しい列であることに注意してください。

  ```sql
  DESC sbtest1000.sbtest9;
  ```

  ```output
  +-------+-------------+------+------+---------+----------------+
  | Field | Type        | Null | Key  | Default | Extra          |
  +-------+-------------+------+------+---------+----------------+
  | id    | int         | NO   | PRI  | NULL    | auto_increment |
  | k     | int         | NO   | MUL  | 0       |                |
  | c     | char(120)   | NO   |      |         |                |
  | pad   | char(60)    | NO   |      |         |                |
  | ed    | varchar(10) | YES  |      | N/A     |                |
  +-------+-------------+------+------+---------+----------------+
  5 rows in set (0.00 sec)
  ```

+ ここまで、TiDB のオンライン DDL 機能を確認しました。これにより、操作を停止することなくスキーマ変更が可能になります。[ターミナル 1](ssh://monitor1-ssh#1) に移動し、<kbd>CTRL-C</kbd> を押してください。その後、両方のターミナルを終了してください。
