## 模块 2：使用 EXPLAIN 命令

+ 在本练习中，你将通过使用 EXPLAIN 命令，研究 TiDB 优化器针对几种不同 SELECT 查询的执行计划。

### 步骤 1. 执行以下语句查看 EXPLAIN 输出

   ```sql
   EXPLAIN SELECT * FROM parameterReference WHERE id=100;
   ```

   ```output
   tidb>EXPLAIN SELECT * FROM parameterReference WHERE id=100;
   +-------------+---------+------+--------------------------+---------------+
   | id          | estRows | task | access object            | operator info |
   +-------------+---------+------+--------------------------+---------------+
   | Point_Get_1 | 1.00    | root | table:parameterReference | handle:100    |
   +-------------+---------+------+--------------------------+---------------+
   1 row in set (0.00 sec)
   ```

+ 此查询的访问方式是什么？
  + 答案：`Point_Get`
+ TiDB 执行此查询必须读取多少行数据？
  + 答案：1
+ 这种访问策略会带来良好的性能吗？为什么？
  + 答案：会。当你将 `PRIMARY KEY（主键）` 或 `UNIQUE Index（唯一索引）` 的所有部分与常量值进行比较时，会应用 `Point_Get` 类型。在这个例子中，表最多有一行匹配的数据。优化器将这一行的列值视为常量，并且只读取一次。在 TiDB 中，`Point_Get` 不需要编译器生成执行计划。由于数据以 key-value 格式存储在 TiKV 中，当 `WHERE` 条件是主键或唯一索引时，我们可以直接使用 `Point_Get` 检索数据。

### 步骤 2. 执行以下语句查看 EXPLAIN 输出

   ```sql
   EXPLAIN SELECT * FROM parameterReference WHERE id=100+5;
   ```

   ```output
   tidb>EXPLAIN SELECT * FROM parameterReference WHERE id=100+5;
   +-------------+---------+------+--------------------------+---------------+
   | id          | estRows | task | access object            | operator info |
   +-------------+---------+------+--------------------------+---------------+
   | Point_Get_5 | 1.00    | root | table:parameterReference | handle:105    |
   +-------------+---------+------+--------------------------+---------------+
   1 row in set (0.00 sec)
   ```

+ 与步骤 1 中查看的查询相比，此查询的执行计划有什么不同吗？为什么？
  + 答案：没有。优化器在执行查询之前会计算表达式 100 + 5。

### 步骤 3. 执行以下语句查看 EXPLAIN 输出

   ```sql
   EXPLAIN SELECT * FROM parameterReference WHERE id-5=100;
   ```

   ```output
   tidb>EXPLAIN SELECT * FROM parameterReference WHERE id-5=100;
   +-------------------------+---------+-----------+--------------------------+---------------------------------------------------+
   | id                      | estRows | task      | access object            | operator info                                     |
   +-------------------------+---------+-----------+--------------------------+---------------------------------------------------+
   | TableReader_7           | 1644.00 | root      |                          | data:Selection_6                                  |
   | └─Selection_6           | 1644.00 | cop[tikv] |                          | eq(minus(universe.parameterreference.id, 5), 100) |
   |   └─TableFullScan_5     | 2055.00 | cop[tikv] | table:parameterReference | keep order:false                                  |
   +-------------------------+---------+-----------+--------------------------+---------------------------------------------------+
   3 rows in set (0.00 sec)
   ```

+ 与步骤 1 和步骤 2 中查看的查询相比，此查询的执行计划有什么不同吗？为什么？
  + 答案：有。优化器在执行查询之前无法将表达式 `ID-5=100` 简化为单个值。因此，它必须全表扫描（类型：TableFullScan）。行数是基于统计信息的估计值，可能与 `parameterReference` 表中的实际行数不相等。

### 步骤 4. 以 JSON 格式显示查询的优化器成本

   ```sql
   CREATE INDEX idx_host_name_ref ON planetarySystem(hostname, pl_name, ref_id);
   EXPLAIN FORMAT = "tidb_json" SELECT pl_name, sy_snum, sy_pnum FROM planetarySystem WHERE pl_name='11 Com b' AND hostname='11 Com' AND ref_id=71\G
   DROP INDEX idx_host_name_ref on planetarySystem;
   ```

   ```output
   *************************** 1. row ***************************
   TiDB_JSON: [
      {
         "id": "Projection_4",
         "estRows": "1.00",
         "taskType": "root",
         "operatorInfo": "universe.planetarysystem.pl_name, universe.planetarysystem.sy_snum, universe.planetarysystem.sy_pnum",
         "subOperators": [
               {
                  "id": "IndexLookUp_10",
                  "estRows": "1.00",
                  "taskType": "root",
                  "subOperators": [
                     {
                           "id": "IndexRangeScan_8(Build)",
                           "estRows": "1.00",
                           "taskType": "cop[tikv]",
                           "accessObject": "table:planetarySystem, index:idx_host_name_ref(hostname, pl_name, ref_id)",
                           "operatorInfo": "range:[\"11 Com\" \"11 Com b\" 71,\"11 Com\" \"11 Com b\" 71], keep order:false"
                     },
                     {
                           "id": "TableRowIDScan_9(Probe)",
                           "estRows": "1.00",
                           "taskType": "cop[tikv]",
                           "accessObject": "table:planetarySystem",
                           "operatorInfo": "keep order:false"
                     }
                  ]
               }
         ]
      }
   ]

   1 row in set (0.01 sec)
   ```

+ 优化器为此查询计算的成本是多少？
  + 答案：1.0（JSON 输出中 "estRows" 字段的值。成本信息 JSON 对象会更详细地展示成本细分。)
+ TiDB 必须访问哪些列来解析查询？
  + 答案： `universe.planetarysystem.pl_name`、`universe.planetarysystem.sy_snum` 和 `universe.planetarysystem.sy_pnum` 列（JSON "operatorInfo" 数组字段中的元素）
+ 在 TiKV 上执行哪些操作？
  + 答案：在 TiKV 上执行 `IndexRangeScan` 和 `TableRowIDScan` 操作（JSON 输出中 `taskType` 字段的值为 `cop[tikv]`）。
+ 在 TiDB Server 上执行哪些操作？
  + 答案：在 TiDB Server 上执行 `IndexLookUp` 操作（JSON 输出中 "taskType" 字段的值为 "root"）。
