## モジュール 3: EXPLAIN ANALYZE コマンドの使用

+ この演習では、TiDBのオプティマイザーの実行計画を複数の異なるSELECTクエリに対してEXPLAIN ANALYZEコマンドを使用して調査します。

### 手順 1. テーブルの作成

+ クラスタ化プライマリキーテーブル`T1`と非クラスタ化プライマリキーテーブル`T2`を作成します。

  ```sql
  DROP TABLE IF EXISTS `T1`;
  CREATE TABLE `T1` (
    `id` bigint AUTO_INCREMENT,
    `name` varchar(20) NOT NULL DEFAULT '',
     PRIMARY KEY (`id`),
     KEY (`name`)
  );
  DROP TABLE IF EXISTS `T2`;
  CREATE TABLE `T2` (
    `id` bigint AUTO_INCREMENT,
    `name` varchar(20) NOT NULL DEFAULT '',
     PRIMARY KEY (`id`) NONCLUSTERED,
     KEY (`name`)
  );
  ```

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

### 手順 2. テーブルにデータを挿入

  ```sql
  INSERT INTO T1 VALUES(NULL, 'Tom');
  INSERT INTO T1 VALUES(NULL, 'Jack');
  INSERT INTO T1 VALUES(NULL, 'Frank');
  INSERT INTO T2 VALUES(NULL, 'Tom');
  INSERT INTO T2 VALUES(NULL, 'Jack');
  INSERT INTO T2 VALUES(NULL, 'Frank');
  ```

  ```output
  Query OK, 1 row affected (0.00 sec)
  ```

### 手順 3. `T1` テーブルに対する `EXPLAIN ANALYZE` の出力を表示

  ```sql
  SELECT id, name from T1;
  EXPLAIN ANALYZE SELECT * FROM T1 WHERE id=1\G
  ```

  ```output
  +----+-------+
  | id | name  |
  +----+-------+
  |  1 | Tom   |
  |  2 | Jack  |
  |  3 | Frank |
  +----+-------+
  3 rows in set (0.00 sec)

  *************************** 1. row ***************************
              id: Point_Get_1
         estRows: 1.00
         actRows: 1
            task: root
   access object: table:T1
  execution info: time:474.1µs, loops:2, RU:0.273086, Get:{num_rpc:1, total_time:443.5µs}, total_process_time: 67.5µs, total_wait_time: 56µs, tikv_wall_time: 149.5µs, scan_detail: {total_process_keys: 1, total_process_keys_size: 39, total_keys: 1, get_snapshot_time: 18.2µs, rocksdb: {block: {cache_hit_count: 2}}}
   operator info: handle:1
          memory: N/A
            disk: N/A
  1 row in set (0.00 sec)
  ```

+ このクエリのアクセス方法は何ですか？
+ 答え: Point_Get
+ このクエリの実際の実行時間はどれくらいですか？
+ 答え: time:474.1µs
+ オペレーター（アクセス方法）は何回実行されましたか？
+ 答え: 2回

### 手順 4. `T1` テーブルに対する `EXPLAIN ANALYZE` の出力を表示

  ```sql
  SELECT id, name from T1;
  EXPLAIN ANALYZE SELECT * FROM T1 WHERE id=4\G
  ```

  ```output
  +----+-------+
  | id | name  |
  +----+-------+
  |  1 | Tom   |
  |  2 | Jack  |
  |  3 | Frank |
  +----+-------+
  3 rows in set (0.00 sec)

  *************************** 1. row ***************************
              id: Point_Get_1
         estRows: 1.00
         actRows: 0
            task: root
   access object: table:T1
  execution info: time:473.8µs, loops:1, RU:0.272975, Get:{num_rpc:1, total_time:446.6µs}, total_process_time: 68.9µs, total_wait_time: 51.7µs, tikv_wall_time: 151.9µs, scan_detail: {total_keys: 1, get_snapshot_time: 17.3µs, rocksdb: {block: {cache_hit_count: 2}}}
   operator info: handle:4
          memory: N/A
            disk: N/A
  1 row in set (0.00 sec)
  ```

+ 以前の手順のクエリと比較して、このクエリの実行計画に差はありますか？なぜですか？ 答え: このクエリの実行時間は473.8µsで、オペレーター（アクセス方法）が実行された回数は1回です。これは、クラスタ化プライマリキーテーブル内に`id=4`の行がないことが確認できると、必要なデータがテーブルに格納されていないことがわかるためです。しかし、以前の手順では、`id=3`の行を見つけた後でも、主キーが`id`であるにもかかわらず、条件を満たす行が存在するかどうかをもう一度確認する必要があります。

### 手順 5. `T2` テーブルに対する `EXPLAIN ANALYZE` の出力を表示

  ```sql
  SELECT _tidb_rowid, id, name from T2;
  EXPLAIN ANALYZE SELECT * FROM T2 WHERE id=1\G
  ```

  ```output
  +-------------+----+-------+
  | _tidb_rowid | id | name  |
  +-------------+----+-------+
  |           2 |  1 | Tom   |
  |           4 |  3 | Jack  |
  |           6 |  5 | Frank |
  +-------------+----+-------+
  3 rows in set (0.00 sec)

  *************************** 1. row ***************************
              id: Point_Get_1
         estRows: 1.00
         actRows: 1
            task: root
   access object: table:T2, index:PRIMARY(id)
  execution info: time:592.9µs, loops:2, RU:0.520601, Get:{num_rpc:2, total_time:565.6µs}, total_process_time: 57.8µs, total_wait_time: 53.1µs, tikv_wall_time: 147.1µs, scan_detail: {total_process_keys: 2, total_process_keys_size: 87, total_keys: 2, get_snapshot_time: 12.6µs, rocksdb: {block: {cache_hit_count: 8}}}
   operator info:
          memory: N/A
            disk: N/A
  1 row in set (0.00 sec)
  ```

+ 手順3と4で見たクエリと比較して、このクエリの実行計画に違いはありますか？理由を述べてください。
+ 答え: `Get:{num_rpc:2, total_time:565.6µs}` フィールドから確認できるように、TiKVに送信されたGet RPCリクエストの数（num_rpc）は2です。これは、`T2` が非クラスタ化プライマリキーテーブルであり、まずプライマリキー `id` から `_tidb_rowid` を取得するために `Point_Get` を使用し、次に `_tidb_rowid` を使用して必要な行をテーブルから取得する必要があるためです。したがって、2つのRPCリクエストが必要です。

### 手順 6. `T2` テーブルに対する `EXPLAIN ANALYZE` の出力を表示

  ```sql
  SELECT _tidb_rowid, id, name from T2;
  EXPLAIN ANALYZE SELECT * FROM T2 WHERE _tidb_rowid=2\G
  ```

  ```output
  +-------------+----+-------+
  | _tidb_rowid | id | name  |
  +-------------+----+-------+
  |           2 |  1 | Tom   |
  |           4 |  3 | Jack  |
  |           6 |  5 | Frank |
  +-------------+----+-------+
  3 rows in set (0.00 sec)

  *************************** 1. row ***************************
              id: Point_Get_1
         estRows: 1.00
         actRows: 1
            task: root
   access object: table:T2
  execution info: time:518.2µs, loops:2, RU:0.274932, Get:{num_rpc:1, total_time:487.5µs}, total_process_time: 72.8µs, total_wait_time: 53.5µs, tikv_wall_time: 158.9µs, scan_detail: {total_process_keys: 1, total_process_keys_size: 43, total_keys: 1, get_snapshot_time: 16.6µs, rocksdb: {block: {cache_hit_count: 4}}}
   operator info: handle:2
          memory: N/A
            disk: N/A
  1 row in set (0.00 sec)
  ```

+ 手順5で見たクエリと比較して、このクエリの実行計画に違いはありますか？理由を述べてください。答え: `Get:{num_rpc:1, total_time:487.5µs}` フィールドから、TiKVに送信されたGet RPCリクエストの数（num_rpc）は1であることがわかります。`T2` テーブルが非クラスタ化プライマリキーテーブルであっても、クエリ条件として `_tidb_rowid` を直接使用する場合は、1つのRPCリクエストだけが必要です。

### 手順 7. テーブル `T1` と `T2` をクリーンアップ

  ```sql
  DROP TABLE T1;
  DROP TABLE T2;
  ```

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