TiDB 查询执行计划概述
SQL 是一种声明式语言。它描述了查询结果应该是什么样子,而不是获取这些结果的方法。TiDB 会考虑所有可能的执行方式,包括使用什么顺序连接表以及是否可以利用潜在的索引。 考虑查询执行计划 的过程被称为 SQL 优化。
EXPLAIN
语句显示给定语句的所选执行计划。也就是说,在考虑了数百或数千种执行方式后,TiDB 认为这个 计划 将会消耗最少的资源并在最短的时间内执行:
CREATE TABLE t (id INT NOT NULL PRIMARY KEY auto_increment, a INT NOT NULL, pad1 VARCHAR(255), INDEX(a));
INSERT INTO t VALUES (1, 1, 'aaa'),(2,2, 'bbb');
EXPLAIN SELECT * FROM t WHERE a = 1;
Query OK, 0 rows affected (0.96 sec)
Query OK, 2 rows affected (0.02 sec)
Records: 2 Duplicates: 0 Warnings: 0
+-------------------------------+---------+-----------+---------------------+---------------------------------------------+
| id | estRows | task | access object | operator info |
+-------------------------------+---------+-----------+---------------------+---------------------------------------------+
| IndexLookUp_10 | 10.00 | root | | |
| ├─IndexRangeScan_8(Build) | 10.00 | cop[tikv] | table:t, index:a(a) | range:[1,1], keep order:false, stats:pseudo |
| └─TableRowIDScan_9(Probe) | 10.00 | cop[tikv] | table:t | keep order:false, stats:pseudo |
+-------------------------------+---------+-----------+---------------------+---------------------------------------------+
3 rows in set (0.00 sec)
EXPLAIN
不会执行实际的查询。可以使用 EXPLAIN ANALYZE
来执行查询并显示 EXPLAIN
信息。这在诊断所选执行计划是否子优化时非常有用。关于使用 EXPLAIN
的更多示例,请参见以下文档:
了解 EXPLAIN 输出
以下描述了上述 EXPLAIN
语句的输出内容:
id
描述执行 SQL 语句所需的操作符或子任务的名称。有关更多细节,请参见 Operator overview。estRows
显示 TiDB 预估将要处理的行数。这个数字可能基于字典信息,例如访问方法基于主键或唯一键,或者基于统计信息,如 CMSketch 或直方图。task
显示操作符执行工作的地点。有关更多细节,请参见 Task overview。access object
显示被访问的表、分区和索引。索引的部分也会显示,例如上例中使用了索引中的列a
。这在你拥有复合索引时非常有用。operator info
显示关于访问的附加细节。有关更多细节,请参见 Operator info overview。
Operator 概述
操作符是执行查询结果返回过程中的特定步骤。执行表扫描(包括磁盘或 TiKV 块缓存)的操作符如下:
- TableFullScan:全表扫描
- TableRangeScan:指定范围的表扫描
- TableRowIDScan:基于 RowID 扫描表数据,通常跟在索引读取操作之后,用于获取匹配的数据行
- IndexFullScan:类似于“全表扫描”,但扫描的是索引而非表数据
- IndexRangeScan:指定范围的索引扫描
TiDB 会对从 TiKV/TiFlash 扫描的数据或计算结果进行聚合。数据聚合操作符可以分为以下几类:
- TableReader:聚合底层操作符(如
TableFullScan
或TableRangeScan
)在 TiKV 中获取的数据 - IndexReader:聚合底层操作符(如
IndexFullScan
或IndexRangeScan
)在 TiKV 中获取的数据 - IndexLookUp:先聚合
Build
端扫描到的 RowID(在 TiKV 中),然后在Probe
端根据这些 RowID 精确读取 TiKV 中的数据。在Build
端有IndexFullScan
或IndexRangeScan
操作符,在Probe
端有TableRowIDScan
操作符 - IndexMerge:类似于
IndexLookUp
。IndexMerge
可以同时读取多个索引。它包含多个Build
和一个Probe
。IndexMerge
的执行过程与IndexLookUp
相同。
虽然结构表现为树形,但执行查询时并不严格要求子节点在父节点之前完成。TiDB 支持查询内的并行执行,因此更准确的描述是子节点 流入 父节点。父、子和兄弟操作符 可能 会在执行查询的不同部分同时进行。
在前例中,├─IndexRangeScan_8(Build)
操作符找到匹配索引 a(a)
的行的内部 RowID
,然后 └─TableRowIDScan_9(Probe)
操作符根据这些 RowID 从表中检索对应的行。
Range 查询
在 WHERE
/ HAVING
/ ON
条件中,TiDB 优化器会分析由主键查询或索引键查询返回的结果。例如,这些条件可能包括数字和日期类型的比较运算符,如 >
, <
, =
, >=
, <=
,以及字符类型的 LIKE
。
Task 概述
目前,TiDB 的计算任务可以分为两类:cop 任务和 root 任务。cop[tikv]
任务表示操作在 TiKV 的 coprocessor 内执行,root
任务表示在 TiDB 内完成。
SQL 优化的目标之一是尽可能将计算下推到 TiKV。TiKV 中的 Coprocessor 支持大部分内置的 SQL 函数(包括聚合函数和标量函数)、SQL LIMIT
操作、索引扫描和表扫描。
Operator info 概述
operator info
可以显示有用的信息,例如哪些条件被成功下推:
range: [1,1]
表示查询的 where 子句中的谓词(a = 1
)已被下推到 TiKV(任务类型为cop[tikv]
)。keep order:false
表示该查询的语义不要求 TiKV 按顺序返回结果。如果查询被修改为需要排序(如SELECT * FROM t WHERE a = 1 ORDER BY id
),则此条件会变为keep order:true
。stats:pseudo
表示estRows
中显示的估算值可能不准确。TiDB 会定期更新统计信息作为后台操作的一部分,也可以通过运行ANALYZE TABLE t
手动更新。
不同的操作符在执行 EXPLAIN
后会输出不同的信息。你可以使用优化器提示(hints)来控制优化器的行为,从而控制物理操作符的选择。例如,/*+ HASH_JOIN(t1, t2) */
表示优化器使用 Hash Join
算法。更多详情请参见 Optimizer Hints。