2.17 执行计划

2.17.1 执行计划的意义

对执行计划进行分析,可以了解中间件和节点是否对SQL语句生成了最优的执行计划,是否有优化的空间,从而为SQL优化提供重要的参考信息。

2.17.2 执行计划的分类

dble的执行计划分为两个层次:dble层的执行计划与节点层的执行计划。
dble层的执行计划: 在SQL语句执行前,dble会根据SQL语句的基本信息,判断该SQL语句应该在哪些节点上执行,将SQL改写成在节点上执行的具体形式,并决定采用何种策略进行数据合并与计算等。
节点层的执行计划: 就是原生的MySQL执行计划。

2.17.3 dble层的执行计划

dble用EXPLAIN指令来查看dble层的执行计划。如例1:

explain select * from test;
+---------------+----------+------------------------------+
| SHARDING_NODE | TYPE     | SQL/REF                      |
+---------------+----------+------------------------------+
| dn1           | BASE SQL | SELECT * FROM test LIMIT 100 |
| dn2           | BASE SQL | SELECT * FROM test LIMIT 100 |
+---------------+----------+------------------------------+
2 rows in set (0.01 sec)

或者例2:

mysql> explain select * from test where id =1;
+---------------+----------+--------------------------------+
| SHARDING_NODE | TYPE     | SQL/REF                        |
+---------------+----------+--------------------------------+
| dn1           | BASE SQL | select * from test where id =1 |
+---------------+----------+--------------------------------+
1 row in set (0.04 sec)

EXPLAIN指令的执行结果包括语句下发的节点,实际下发的SQL语句和数据的合并操作的信息。这些信息是系统静态分析产生的,并没有真正的执行语句。

另外,复杂查询的查询计划也会有所反映,可以通过计划来优化查询语句
如例3:

mysql> explain select * from sharding_two_node a inner join sharding_four_node b on a.id =b.id;

+---------------+----------+----------------------------------------------------------------------------------------------------+
| SHARDING_NODE | TYPE     | SQL/REF                                                                                            |
+---------------+----------+----------------------------------------------------------------------------------------------------+
| dn1.0         | BASE SQL | select `a`.`id`,`a`.`c_char`,`a`.`ts`,`a`.`si` from  `sharding_two_node` `a` ORDER BY `a`.`id` ASC |
| dn2.0         | BASE SQL | select `a`.`id`,`a`.`c_char`,`a`.`ts`,`a`.`si` from  `sharding_two_node` `a` ORDER BY `a`.`id` ASC |
| dn1.1         | BASE SQL | select `b`.`id`,`b`.`c_flag`,`b`.`c_decimal` from  `sharding_four_node` `b` ORDER BY `b`.`id` ASC  |
| dn2.1         | BASE SQL | select `b`.`id`,`b`.`c_flag`,`b`.`c_decimal` from  `sharding_four_node` `b` ORDER BY `b`.`id` ASC  |
| dn3.0         | BASE SQL | select `b`.`id`,`b`.`c_flag`,`b`.`c_decimal` from  `sharding_four_node` `b` ORDER BY `b`.`id` ASC  |
| dn4.0         | BASE SQL | select `b`.`id`,`b`.`c_flag`,`b`.`c_decimal` from  `sharding_four_node` `b` ORDER BY `b`.`id` ASC  |
| merge.1       | MERGE    | dn1.0, dn2.0                                                                                       |
| merge.2       | MERGE    | dn1.1, dn2.1, dn3.0, dn4.0                                                                         |
| join.1        | JOIN     | merge.1, merge.2                                                                                   |
+---------------+----------+----------------------------------------------------------------------------------------------------+
9 rows in set (0.00 sec)

再举例4:

mysql> explain select id from single union all select b.si from sharding_four_node a inner join  sharding_two_node b on a.id =b.id
+-----------------+-----------+------------------------------------------------------------------------------+
| SHARDING_NODE   | TYPE      | SQL/REF                                                                      |
+-----------------+-----------+------------------------------------------------------------------------------+
| dn1.0           | BASE SQL  | select `single`.`id` from  `single`                                          |
| dn1.1           | BASE SQL  | select `a`.`id` from  `sharding_four_node` `a` ORDER BY `a`.`id` ASC         |
| dn2.0           | BASE SQL  | select `a`.`id` from  `sharding_four_node` `a` ORDER BY `a`.`id` ASC         |
| dn3.0           | BASE SQL  | select `a`.`id` from  `sharding_four_node` `a` ORDER BY `a`.`id` ASC         |
| dn4.0           | BASE SQL  | select `a`.`id` from  `sharding_four_node` `a` ORDER BY `a`.`id` ASC         |
| dn1.2           | BASE SQL  | select `b`.`si`,`b`.`id` from  `sharding_two_node` `b` ORDER BY `b`.`id` ASC |
| dn2.1           | BASE SQL  | select `b`.`si`,`b`.`id` from  `sharding_two_node` `b` ORDER BY `b`.`id` ASC |
| merge.2         | MERGE     | dn1.1, dn2.0, dn3.0, dn4.0                                                   |
| merge.3         | MERGE     | dn1.2, dn2.1                                                                 |
| join.1          | JOIN      | merge.2, merge.3                                                             |
| merge.1         | MERGE     | dn1.0                                                                        |
| union_all.1     | UNION_ALL | join.1, merge.1                                                              |
+-----------------+-----------+------------------------------------------------------------------------------+
12 rows in set (0.01 sec)

我们看到,查询计划分为3列,分别是SHARDING_NODE,TYPE和SQL/REF。

2.17.3.1 简单查询计划的解读

首先,我们要建立一个概念,查询计划结果分两类,一类是简单查询,如例1例2,中间件只做路由计算,所以查询计划比较简单。
这种场景下,

  • SHARDING_NODE列的内容对应于sharding.xml中的shardingNode的name属性,表明sql将被发往哪里。
  • TYPE列恒为BASE SQL,表示这是个基本查询
  • SQL/REF列实际退化为SQL,标识了实际下发的SQL内容,看例1的无条件广播实际就被改写,增加了limit 100(当然这个功能也可以设置或者关闭)。

2.17.3.2 复杂查询计划的解读

另一类是复杂查询,这类查询其实需要dble进行计算,比如例3中的跨库join,实际是构建一个类似这样的查询树。

                            ┌────────────┐
                            │            │
                   ┌────────►    join.1  ◄─────────┐
                   │        │            │         │
                   │        └────────────┘         │
                   │                               │
             ┌─────┴─────┐                   ┌─────┴─────┐
             │           │                   │           │
    ┌────────►   merge.1 │    ┌──────────────►   merge.2 ◄────────┐
    │        │           │    │              │           │        │
    │        └─────▲─────┘    │              └──▲───▲────┘        │
    │              │          │                 │   │             │
    │              │          │                 │   │             │
┌───┴───┐   ┌──────┴─┐   ┌────┴───┐   ┌────────┬┘ ┌─┴─────┐  ┌────┴──┐
│       │   │        │   │        │   │        │  │       │  │       │
│ dn1.0 │   │ dn2.0  │   │  dn1.1 │   │  dn2.1 │  │ dn3.0 │  │ dn4.0 │
└───────┘   └────────┘   └────────┘   └────────┘  └───────┘  └───────┘

查询树更多细节请参考公开课相关章节。 所以,理解了查询树,就能理解查询计划了。

  • SHARDING_NODE列的内容分两类,一类对应于sharding.xml中的shardingNode的name属性,表明sql将被发往哪里,对应查询树的叶子结点,和简单查询类似,另一类是查询树的非叶子结点,代表了在dble中需要计算的算子的名称。 由于同一个结点可能有多次查询下发。同名算子也可能有多个,所以用点号和自增序列区别。
  • TYPE列:叶子结点为BASE SQL,表示这是下发的基本查询,非叶子结点表示是算子的类型:算子包括:
    • MERGE:合并
    • MERGE_AND_ORDER:带排序的合并(归并排序)
    • AGGREGATE:聚合
    • DISTINCT:去重
    • LIMIT:取前n行
    • WHERE_FILTER:where过滤
    • HAVING_FILTER:having过滤
    • SHUFFLE_FIELD:列名&数据类型整理
    • UNION_ALL:union all操作,如果原sql是union,会裂为UNION_ALL和DISTINCT两个算子
    • ORDER:排序
    • NOT_IN:not in 处理。
    • JOIN:join操作
    • DIRECT_GROUP:直接group by。
    • NEST_LOOP:NEST_LOOP 类型的join,结果返回处理
    • IN_SUB_QUERY:in子查询结果返回处理。
    • ALL_ANY_SUB_QUERY:all/any子查询结果返回处理。
    • SCALAR_SUB_QUERY:标量子查询结果返回处理
    • RENAME_DERIVED_SUB_QUERY:DERIVED子查询结果处理。
    • INNER_FUNC_ADD: sql包含一些dble特殊处理的函数时的处理逻辑,例如LAST_INSERT_ID
    • INNER_FUNC_MERGE:sql的select仅包含dble特殊处理的函数时的处理逻辑,例如LAST_INSERT_ID。
    • for CHILD in UPDATE_SUB_QUERY.RESULTS:update多表场景中,遍历先下发的select语句的结果集,填充到update单表语句后下发。
    • MERGE UPDATE:update多表场景中,回收整理循环下发的结果
  • SQL/REF列:叶子结点退化为SQL,标识了实际下发的SQL内容;非叶子结点表达了查询树中子节点的名称,对应SHARDING_NODE列的名称。通过这样,就可以通过查询计划画出一颗完整的查询树来,也就能了解分布式查询计划的细节了。

2.17.4 节点层的执行计划

通过EXPLAIN2命令可查看指定节点上的执行计划。如:

mysql> explain2 shardingnode=dn1 sql=select * from test where id =1;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | test  | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    1 |   100.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.01 sec)

explain2会将sql语句加上explain下发到指定的shardingnode执行,并把节点上explain的结果返回客户端,可以用于观察单个节点的sql执行计划。

results matching ""

    No results matching ""