Search

# 执行计划和基本算子

1. 通过 `EXPLAIN` 指令查看执行计划，或通过 `EXPLAIN ANALYZE` 查看实际执行情况（参见查询执行器介绍章节），分析问题所在
2. 尝试通过 Hint 控制优化器行为，将执行计划修改成我们期望的样子

## 执行计划与 EXPLAIN 命令

EXPLAIN 语法如下（注意 Hint 需要放在 EXPLAIN 之后）：

``EXPLAIN <SQL Statement>``EXPLAIN <Hint> <SQL Statement>``

``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``

``mysql> 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)``

## 算子介绍

DRDS 中支持以下算子：

### LogicalView

LogicalView 是从MySQL数据源拉取数据的算子，类似于其他数据库中的 TableScan 或 IndexScan，但支持更多的下推。LogicalView 中包含下推的 SQL 语句和数据源信息，更像一个“视图”。其中下推的 SQL 可能包含多种算子，如 Project、Filter、聚合、排序、Join 和子查询等。

``> 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：底层数据源对应的表名，以 `.` 分割，`.` 之前是分库对应的编号，`.` 之后是表名及其编号，如 `[000-127]` 表示表名编号从 000 到 127 的所有表。
• shardCount：需要访问的分表总数，该示例中会访问从 000 到 127 共计 128 张分表。
• sql：下发至底层数据源的 SQL 模版，DRDS 在执行时会将表名替换为物理表名，参数化的常量（`?`）会被替换成实际参数（详见执行计划管理一章）。

### Gather

Gather 将多份数据合并成同份数据。上面的例子中，Gather 将各个分表上查询到的数据合并成一份。

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`")``

### UnionAll 与 UnionDistinct

``> 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

``> 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 模版。同样，由于开启了 PlanCache，对 SQL 做了参数化处理，SQL 模版中的常量会用 `?` 替换。

### PhyTableOperation

PhyTableOperation 表示对某个物理分表直接执行一个操作，该算子目前仅用于 INSERT 语句。

``> 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")``

• tables：物理表名，仅有唯一一个物理表名。
• sql：SQL 模版，该 SQL 模版中表名和常量均被参数化，用 ? 替换，对应的参数在随后的 params 中给出。
• params：SQL 模版对应的参数，包括表名和常量。