Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

explain: add index merge (#4576) #4661

Merged
merged 1 commit into from
Oct 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions explain-index-merge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
title: 用 EXPLAIN 查看开启 IndexMerge 的 SQL 执行计划
summary: 了解 TiDB 中 EXPLAIN 语句返回的执行计划信息。
---

# 用 EXPLAIN 查看开启 IndexMerge 的 SQL 执行计划

`IndexMerge` 是 TiDB v4.0 中引入的一种对表的新访问方式。在这种访问方式下,TiDB 优化器可以选择对一张表使用多个索引,并将每个索引的返回结果进行合并。在某些场景下,这种访问方式能够减少大量不必要的数据扫描,提升查询的执行效率。

```sql
EXPLAIN SELECT * from t where a = 1 or b = 1;
+-------------------------+----------+-----------+---------------+--------------------------------------+
| id | estRows | task | access object | operator info |
+-------------------------+----------+-----------+---------------+--------------------------------------+
| TableReader_7 | 8000.00 | root | | data:Selection_6 |
| └─Selection_6 | 8000.00 | cop[tikv] | | or(eq(test.t.a, 1), eq(test.t.b, 1)) |
| └─TableFullScan_5 | 10000.00 | cop[tikv] | table:t | keep order:false, stats:pseudo |
+-------------------------+----------+-----------+---------------+--------------------------------------+
set @@tidb_enable_index_merge = 1;
explain select * from t use index(idx_a, idx_b) where a > 1 or b > 1;
+--------------------------------+---------+-----------+-------------------------+------------------------------------------------+
| id | estRows | task | access object | operator info |
+--------------------------------+---------+-----------+-------------------------+------------------------------------------------+
| IndexMerge_16 | 6666.67 | root | | |
| ├─IndexRangeScan_13(Build) | 3333.33 | cop[tikv] | table:t, index:idx_a(a) | range:(1,+inf], keep order:false, stats:pseudo |
| ├─IndexRangeScan_14(Build) | 3333.33 | cop[tikv] | table:t, index:idx_b(b) | range:(1,+inf], keep order:false, stats:pseudo |
| └─TableRowIDScan_15(Probe) | 6666.67 | cop[tikv] | table:t | keep order:false, stats:pseudo |
+--------------------------------+---------+-----------+-------------------------+------------------------------------------------+
```

例如,在上述示例中,过滤条件是使用 `OR` 条件连接的 `WHERE` 子句。在启用 `IndexMerge` 前,每个表只能使用一个索引,不能将 `a = 1` 下推到索引 `a`,也不能将 `b = 1` 下推到索引 `b`。当 `t` 中存在大量数据时,全表扫描的效率会很低。针对这类场景,TiDB 引入了对表的新访问方式 `IndexMerge`。

在 `IndexMerge` 访问方式下,优化器可以选择对一张表使用多个索引,并将每个索引的返回结果进行合并,生成以上示例中后一个 `IndexMerge` 的执行计划。此时的 `IndexMerge_16` 算子有三个子节点,其中 `IndexRangeScan_13` 和 `IndexRangeScan_14` 根据范围扫描得到符合条件的所有 `RowID`,再由 `TableRowIDScan_15` 算子根据这些 `RowID` 精确地读取所有满足条件的数据。

其中对于 `IndexRangeScan`/`TableRangeScan` 一类按范围进行的扫表操作,`EXPLAIN` 表中 `operator info` 列相比于其他扫表操作,多了被扫描数据的范围这一信息。比如上面的例子中,`IndexRangeScan_13` 算子中的 `range:(1,+inf]` 这一信息表示该算子扫描了从 1 到正无穷这个范围的数据。

> **注意:**
>
> 目前,TiDB 的 `IndexMerge` 特性在 TiDB 4.0.0-rc.1 版本中默认关闭。同时 4.0 版本中的 `IndexMerge` 目前支持的场景仅限于析取范式(`or` 连接的表达式),暂不支持合取范式(`and` 连接的表达式)。开启 `IndexMerge` 特性有以下方法:
>
> - 设置系统变量 `tidb_enable_index_merge=1`;
>
> - 在查询中使用 SQL 优化器 Hint [`USE_INDEX_MERGE`](/optimizer-hints.md#use_index_merget1_name-idx1_name--idx2_name-)。
>
> SQL Hint 的优先级高于系统变量。
17 changes: 9 additions & 8 deletions query-execution-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ mysql> explain select * from t where a > 1 or b >100;

#### IndexMerge 示例

IndexMerge 是在 TiDB 4.0 引入的一种对表的新访问方式。在 IndexMerge 访问方式下,优化器可以选择对一张表使用多个索引,并将每个索引的返回结果进行合并。在某些场景下,这种访问方式能够减少大量不必要的数据扫描,提升查询的执行效率。
`IndexMerge` 是 TiDB v4.0 中引入的一种对表的新访问方式。在这种访问方式下,TiDB 优化器可以选择对一张表使用多个索引,并将每个索引的返回结果进行合并。在某些场景下,这种访问方式能够减少大量不必要的数据扫描,提升查询的执行效率。

```
mysql> explain select * from t where a = 1 or b = 1;
Expand All @@ -171,19 +171,20 @@ mysql> explain select * from t use index(idx_a, idx_b) where a > 1 or b > 1;
+--------------------------------+---------+-----------+-------------------------+------------------------------------------------+
```

比如在上面给出的样例中,在未使用 IndexMerge 前,由于查询的过滤条件是一个通过 `OR` 连接的表达式,我们在只能对每张表使用一个索引的限制下,无法将 `a = 1` 下推到索引 `a` 上,或将 `b = 1` 下推到索引 `b` 上,这样当 `t` 的数据量很大时,全表扫描的效率会很低,但这条查询最多却只会返回两行记录。针对这类场景,TiDB 引入了对表的新访问方式 IndexMerge。
例如,在上述示例中,过滤条件是使用 `OR` 条件连接的 `WHERE` 子句。在启用 `IndexMerge` 前,每个表只能使用一个索引,不能将 `a = 1` 下推到索引 `a`,也不能将 `b = 1` 下推到索引 `b`。当 `t` 中存在大量数据时,全表扫描的效率会很低。针对这类场景,TiDB 引入了对表的新访问方式 `IndexMerge`

在 IndexMerge 访问方式下,优化器可以选择对一张表使用多个索引,并将每个索引的返回结果进行集合并操作,生成上图中后一个 IndexMerge 的执行计划。这里 `IndexMerge_16` 算子有三个孩子节点,其中 `IndexRangeScan_13` 和 `IndexRangeScan_14` 根据范围扫描得到符合条件的所有 RowID,再由 `TableRowIDScan_15` 算子根据这些 RowID 精确地读取所有满足条件的数据。
`IndexMerge` 访问方式下,优化器可以选择对一张表使用多个索引,并将每个索引的返回结果进行合并,生成以上示例中后一个 `IndexMerge` 的执行计划。此时的 `IndexMerge_16` 算子有三个子节点,其中 `IndexRangeScan_13` 和 `IndexRangeScan_14` 根据范围扫描得到符合条件的所有 `RowID`,再由 `TableRowIDScan_15` 算子根据这些 `RowID` 精确地读取所有满足条件的数据。

其中对于 IndexRangeScan/TableRangeScan 一类按范围进行的扫表操作来说,explain 表中 operator info 列相比于其他扫表操作,多了被扫描数据的范围这一信息。如在上面的例子中,IndexRangeScan 算子中的 `range:(1,+inf]` 这一信息表示该算子扫描了从 1 到正无穷这个范围的数据。
其中对于 `IndexRangeScan`/`TableRangeScan` 一类按范围进行的扫表操作,`EXPLAIN` 表中 `operator info` 列相比于其他扫表操作,多了被扫描数据的范围这一信息。比如上面的例子中,`IndexRangeScan_13` 算子中的 `range:(1,+inf]` 这一信息表示该算子扫描了从 `1` 到正无穷这个范围的数据。

> **注意:**
>
> 目前 TiDB 的 IndexMerge 特性在 4.0 RC 版本中默认关闭,同时 4.0 中的 IndexMerge 目前支持的场景仅限于析取范式(or 连接的表达式),对合取范式(and 连接的表达式)将在之后的版本中支持。
> 开启 IndexMerge 特性的方法有两种:
> 目前,TiDB 的 `IndexMerge` 特性在 TiDB 4.0.0-rc.1 版本中默认关闭。同时 4.0 版本中的 `IndexMerge` 目前支持的场景仅限于析取范式(`or` 连接的表达式),暂不支持合取范式(`and` 连接的表达式)。开启 `IndexMerge` 特性有以下方法:
>
> - 设置系统变量 `tidb_enable_index_merge` 为 1;
> - 在查询中使用 SQL Hint [`USE_INDEX_MERGE`](/optimizer-hints.md#use_index_merget1_name-idx1_name--idx2_name-);注意:SQL Hint 的优先级高于系统变量。
> - 设置系统变量 `tidb_enable_index_merge` 为 1
> - 在查询中使用 SQL Hint [`USE_INDEX_MERGE`](/optimizer-hints.md#use_index_merget1_name-idx1_name--idx2_name-)
>
> SQL Hint 的优先级高于系统变量。

### 如何阅读聚合的执行计划

Expand Down