本文介绍如何使用EXPLAIN
命令查询执行计划,并介绍一些基本的算子(例如LogicalView
,Gather
,MergeSort
等)。更多算子(例如Join、Agg、Sort等)的介绍在后续的章节中单独列出。
背景信息
通常SQL调优的过程离不开以下两个步骤:
- 分析问题,例如通过
EXPLAIN
命令查看执行计划,您也可以通过EXPLAIN ANALYZE
查看实际执行情况来分析问题。(参见查询执行器介绍章节)。 - 通过
Hint
控制优化器行为,修改执行计划。
执行计划与EXPLAIN
命令
下述案例介绍如何通过EXPLAIN
命令获取查询的执行计划。
EXPLAIN
语法如下:
EXPLAIN <SQL Statement>
EXPLAIN <Hint> <SQL Statement>
Hint
需放在EXPLAIN
之后。
本文中的示例均基于以下表结构:
CREATE TABLE `sbtest1` (
`id` INT(10) UNSIGNED NOT NULL,
`k` INT(10) UNSIGNED NOT NULL DEFAULT '0',
`c` CHAR(120) NOT NULL DEFAULT '',
`pad` CHAR(60) NOT NULL DEFAULT '',
KEY `xid` (`id`),
KEY `k_1` (`k`)
) dbpartition BY HASH (`id`) tbpartition BY HASH (`id`) tbpartitions 4
执行以下EXPLAIN
命令,PolarDB-X 1.0将返回对应的执行计划。
explain select a.k, count(*) cnt from sbtest1 a, sbtest1 b where a.id = b.k and a.id > 1000 group by k having cnt > 1300 order by cnt limit 5, 10;
+---------------------------------------------------------------------------------------------------------------------------------------------------+
| LOGICAL PLAN |
+---------------------------------------------------------------------------------------------------------------------------------------------------+
| MemSort(sort="cnt ASC", offset=?2, fetch=?3) |
| Filter(condition="cnt > ?1") |
| HashAgg(group="k", cnt="COUNT()") |
| BKAJoin(id="id", k="k", c="c", pad="pad", id0="id0", k0="k0", c0="c0", pad0="pad0", condition="id = k", type="inner") |
| MergeSort(sort="k ASC") |
| LogicalView(tables="[0000-0031].sbtest1_[000-127]", shardCount=128, sql="SELECT * FROM `sbtest1` WHERE (`id` > ?) ORDER BY `k`") |
| Gather(concurrent=true) |
| LogicalView(tables="[0000-0031].sbtest1_[000-127]", shardCount=128, sql="SELECT * FROM `sbtest1` WHERE ((`k` > ?) AND (`k` IN ('?')))") |
| HitCache:false |
+---------------------------------------------------------------------------------------------------------------------------------------------------+
9 rows in set (0.01 sec)
除执行计划外,EXPLAIN
结果中还会有一些额外信息,上面的例子中仅有一项HitCache
(是否命中Plan Cache缓存),详细原理参见执行计划管理。
算子介绍
PolarDB-X 1.0中支持以下算子。
含义 | 物理算子 |
---|---|
下发查询 | LogicalView ,LogicalModifyView ,PhyTableOperation |
连接(Join) | BKAJoin ,NLJoin ,HashJoin ,SortMergeJoin ,HashSemiJoin ,SortMergeSemiJoin ,MaterializedSemiJoin |
排序 | MemSort ,TopN |
聚合(Group By) | HashAgg ,SortAgg |
数据交换 | Gather ,MergeSort |
其它 | Project ,Filter , Limit ,Union ,Window |
以下介绍部分算子的含义和实现,剩余的部分在后面的章节中介绍。
LogicalView
LogicalView
是从存储层MySQL数据源拉取数据的算子,类似于其他数据库中的TableScan或IndexScan,但支持更多的下推。LogicalView
中包含下推的SQL语句和数据源信息,更像一个视图。其中下推的SQL可能包含多种算子,如Project、Filter、聚合、排序、Join和子查询等。下述示例为您展示EXPLAIN
中LogicalView
的输出信息及其含义:
> explain select * From sbtest1 where id > 1000;
Gather(concurrent=true)
LogicalView(tables="[0000-0031].sbtest1_[000-127]", shardCount=128, sql="SELECT * FROM `sbtest1` WHERE (`id` > ?)")
LogicalView
的信息由三部分构成:
tables
:存储层MySQL对应的表名,以英文句号(.)分割,英文句号(.)之前是分库对应的编号,之后是表名及其编号,如[000-127]
表示表名编号从000到127的所有表。shardCount
:需访问的分表总数,该示例会访问从000到127共计128张分表。sql
:下发至存储层MySQL的SQL模版,PolarDB-X 1.0在执行时会将表名替换为物理表名,参数化的常量问号(?)替换成实际参数,详情请参见执行计划管理。
Gather
Gather
将多份数据合并成同份数据。上面的例子中,Gather
将各个分表上查询到的数据合并成一份。
在不使用并行查询时,Gather
通常出现在LogicalView
上方,表示收集合并各个分表的数据。如果并行查询开启,Gather
可能被上拉到执行计划中更高的地方,这时候Gather
表示将各个Worker的计算结果收集合并。
Gather
中的concurrent
表示是否并发执行子算子,默认为true
,表示并发拉取数据。开启并行查询时,上拉的Gather
属性有所变化,显示为parallel=true
。
MergeSort
MergeSort
即归并排序算子,表示将有序的数据流进行归并排序,合并成一个有序的数据流。例如:
> explain select * from sbtest1 where id > 1000 order by id limit 5,10;
MergeSort(sort="id ASC", offset=?1, fetch=?2)
LogicalView(tables="[0000-0031].sbtest1_[000-127]", shardCount=128, sql="SELECT * FROM `sbtest1` WHERE (`id` > ?) ORDER BY `id` LIMIT (? + ?)")
MergeSort
算子包含三部分内容:
sort
:表示排序字段以及排列顺序,id ASC
表示按照ID字段递增排序,DESC
表示递减排序。offset
:表示获取结果集时的偏移量,例子中被参数化了,实际值为5。fetch
:表示最多返回的数据行数。与offset类似,同样是参数化的表示,实际对应的值为10。
Project
Project
表示投影操作,即从输入数据中选择部分列输出,或者对某些列进行转换(通过函数或者表达式计算)后输出,当然也可以包含常量。
> explain select '你好, DRDS', 1 / 2, CURTIME();
Project(你好, DRDS="_UTF-16'你好, DRDS'", 1 / 2="1 / 2", CURTIME()="CURTIME()")
Project
的计划中包括每列的列名及其对应的列、值、函数或者表达式。
Filter
Filter
表示过滤操作,其中包含一些过滤条件。该算子对输入数据进行过滤,若满足条件,则输出,否则丢弃。如下是一个较复杂的例子,包含了以上介绍的大部分算子。
> explain select k, avg(id) avg_id from sbtest1 where id > 1000 group by k having avg_id > 1300;
Filter(condition="avg_id > ?1")
Project(k="k", avg_id="sum_pushed_sum / sum_pushed_count")
SortAgg(group="k", sum_pushed_sum="SUM(pushed_sum)", sum_pushed_count="SUM(pushed_count)")
MergeSort(sort="k ASC")
LogicalView(tables="[0000-0031].sbtest1_[000-127]", shardCount=128, sql="SELECT `k`, SUM(`id`) AS `pushed_sum`, COUNT(`id`) AS `pushed_count` FROM `sbtest1` WHERE (`id` > ?) GROUP BY `k` ORDER BY `k`")
WHERE id > 1000
中的条件没有对应的Filter算子,是因为这个算子最终被下推到了LogicalView
中,可以在LogicalView
的SQL中看到WHERE (id > ?)
。
Union All与Union Distinct
顾名思义,Union All对应UNIONALL
,Union Distinct对应UNIONDISTINCT
。该算子通常有2个或更多个输入,表示将多个输入的数据合并在一起。例如:
> explain select * From sbtest1 where id > 1000 union distinct select * From sbtest1 where id < 200;
UnionDistinct(concurrent=true)
Gather(concurrent=true)
LogicalView(tables="[0000-0031].sbtest1_[000-127]", shardCount=128, sql="SELECT * FROM `sbtest1` WHERE (`id` > ?)")
Gather(concurrent=true)
LogicalView(tables="[0000-0031].sbtest1_[000-127]", shardCount=128, sql="SELECT * FROM `sbtest1` WHERE (`id` < ?)")
LogicalModifyView
如上文介绍,LogicalView
表示从底层数据源获取数据的算子,与之对应的,LogicalModifyView
表示对底层数据源的修改算子,其中也会记录一个SQL语句,该SQL可能是INSERT
、UPDATE
或者DELETE
。
> explain update sbtest1 set c='Hello, DRDS' where id > 1000;
LogicalModifyView(tables="[0000-0031].sbtest1_[000-127]", shardCount=128, sql="UPDATE `sbtest1` SET `c` = ? WHERE (`id` > ?)"
> explain delete from sbtest1 where id > 1000;
LogicalModifyView(tables="[0000-0031].sbtest1_[000-127]", shardCount=128, sql="DELETE FROM `sbtest1` WHERE (`id` > ?)")
LogicalModifyView
查询计划的内容与LogicalView
类似,包括下发的物理分表,分表数以及SQL模版。同样,由于开启了执行计划缓存,对SQL做了参数化处理,SQL模版中的常量会用?
替换。
PhyTableOperation
PhyTableOperation
表示对某个物理分表直接执行一个操作。
> explain insert into sbtest1 values(1, 1, '1', '1'),(2, 2, '2', '2');
PhyTableOperation(tables="SYSBENCH_CORONADB_1526954857179TGMMSYSBENCH_CORONADB_VGOC_0000_RDS.[sbtest1_001]", sql="INSERT INTO ? (`id`, `k`, `c`, `pad`) VALUES(?, ?, ?, ?)", params="`sbtest1_001`,1,1,1,1")
PhyTableOperation(tables="SYSBENCH_CORONADB_1526954857179TGMMSYSBENCH_CORONADB_VGOC_0000_RDS.[sbtest1_002]", sql="INSERT INTO ? (`id`, `k`, `c`, `pad`) VALUES(?, ?, ?, ?)", params="`sbtest1_002`,2,2,2,2")
示例中,INSERT插入两行数据,每行数据对应一个PhyTableOperation
算子。PhyTableOperation
算子的内容包括三部分:
tables
:物理表名,仅有唯一一个物理表名。sql
:SQL模版,该SQL模版中表名和常量均被参数化,用?
替换,对应的参数在随后的params
中给出。params
:SQL模版对应的参数,包括表名和常量。