Apache Doris执行计划:EXPLAIN输出详解

Apache Doris执行计划:EXPLAIN输出详解

【免费下载链接】doris Apache Doris is an easy-to-use, high performance and unified analytics database. 【免费下载链接】doris 项目地址: https://gitcode.com/gh_mirrors/dori/doris

你是否在使用Apache Doris时遇到查询性能瓶颈?是否想优化SQL却不知从何下手?本文将带你深入理解EXPLAIN命令的输出结果,掌握执行计划分析技巧,轻松定位查询效率问题。读完本文,你将能够:

  • 理解EXPLAIN输出的基本结构
  • 识别常见的执行算子
  • 学会分析执行计划并发现优化点
  • 掌握使用Nereids Planner的高级技巧

什么是执行计划

执行计划(Execution Plan)是Apache Doris查询优化器为SQL语句生成的执行方案,它描述了查询将如何被执行,包括数据读取方式、算子选择、连接顺序等关键信息。通过EXPLAIN命令,我们可以查看执行计划,从而了解查询的内部执行流程,为性能优化提供依据。

在Apache Doris中,执行计划由一系列执行算子(Operator)组成,每个算子负责特定的数据处理任务。这些算子按一定的顺序组合,形成一个有向无环图(DAG),数据从下往上流动,经过各个算子的处理后得到最终结果。

如何生成执行计划

在Apache Doris中,使用EXPLAIN命令可以生成SQL语句的执行计划。基本语法如下:

EXPLAIN SELECT ...;

例如,我们可以对一个简单的查询生成执行计划:

EXPLAIN SELECT v1.k FROM test_view v1 LEFT JOIN test_view v2 ON v1.k = v2.k;

示例来源:pytest/qe/query_regression/sql/issue_4241.sql

此外,Doris还支持CTE(Common Table Expression)语法的执行计划展示:

EXPLAIN WITH test_view (k) AS ( SELECT NULL AS k UNION ALL SELECT NULL AS k ) 
SELECT v1.k FROM test_view v1 LEFT JOIN test_view v2 ON v1.k = v2.k;

示例来源:pytest/qe/query_regression/sql/issue_4241.sql

执行计划基本结构

Apache Doris的执行计划输出通常包含以下几个部分:

  1. ID:算子的唯一标识符
  2. OPERATOR:算子名称
  3. NAME:算子的具体名称或描述
  4. EST. ROWS:估计的输出行数
  5. EST. BYTES:估计的输出数据量
  6. PROPERTIES:算子的属性信息

下面是一个典型的执行计划输出示例:

+-------------------------------------------------------------------------------------------------+
| ID | OPERATOR           | NAME       | EST. ROWS | EST. BYTES | PROPERTIES                          |
+-------------------------------------------------------------------------------------------------+
| 0  | EXCHANGE           | UNPARTITION | 10000     | 400000     | TYPE: GATHER                        |
| 1  |  AGGREGATE         | SUM        | 10000     | 400000     | group by: k, sum: v                 |
| 2  |   EXCHANGE         | HASH       | 100000    | 4000000    | HASH KEYS: k                        |
| 3  |    AGGREGATE       | PARTIAL_SUM| 100000    | 4000000    | group by: k, sum: v                 |
| 4  |     SCAN           | OLAP_TABLE | 1000000   | 40000000   | table: test, partitions: [p1], buckets: [1-10] |
+-------------------------------------------------------------------------------------------------+

这个执行计划展示了一个聚合查询的执行流程,从最底层的SCAN算子读取数据,到中间的AGGREGATE算子进行部分聚合,再通过EXCHANGE算子进行数据重分布,最后进行全局聚合并返回结果。

常见执行算子解析

1. SCAN算子

SCAN算子负责从存储引擎读取数据,是执行计划的起点。常见的SCAN算子有:

  • OLAP_TABLE_SCAN:从Doris的OLAP表中读取数据
  • MYSQL_SCAN:从MySQL外部表读取数据
  • HIVE_SCAN:从Hive外部表读取数据
  • ES_SCAN:从Elasticsearch外部表读取数据

SCAN算子的PROPERTIES中通常包含表名、分区信息、分桶信息等。例如:

PROPERTIES: {
  "table": "test_table",
  "partitions": ["p202301", "p202302"],
  "buckets": ["1-10"],
  "predicates": "dt >= '2023-01-01'"
}

2. AGGREGATE算子

AGGREGATE算子负责执行聚合操作,如COUNT、SUM、AVG等。常见的AGGREGATE算子有:

  • PARTIAL_AGGREGATE:部分聚合,在数据分片上进行初步聚合
  • FINAL_AGGREGATE:最终聚合,对部分聚合的结果进行全局聚合
  • STREAMING_AGGREGATE:流式聚合,适用于已排序的数据

AGGREGATE算子的PROPERTIES中会显示聚合函数和分组键信息,例如:

PROPERTIES: {
  "group by": "user_id",
  "functions": "count(*), sum(amount)"
}

3. JOIN算子

JOIN算子负责执行表连接操作,常见的JOIN算子有:

  • HASH_JOIN:哈希连接,适用于大表连接
  • MERGE_JOIN:合并连接,适用于已排序的表连接
  • NESTED_LOOP_JOIN:嵌套循环连接,适用于小表连接

JOIN算子的PROPERTIES中会显示连接类型、连接条件等信息,例如:

PROPERTIES: {
  "join_type": "INNER JOIN",
  "condition": "a.id = b.user_id",
  "left_table": "orders",
  "right_table": "users"
}

4. EXCHANGE算子

EXCHANGE算子负责在不同节点间传输数据,实现数据重分布。常见的EXCHANGE算子有:

  • HASH_EXCHANGE:根据哈希值进行数据重分布
  • RANGE_EXCHANGE:根据范围进行数据重分布
  • BROADCAST_EXCHANGE:将数据广播到所有节点
  • GATHER_EXCHANGE:将数据收集到一个节点

EXCHANGE算子的PROPERTIES中会显示数据重分布的方式和关键字,例如:

PROPERTIES: {
  "type": "HASH",
  "keys": "user_id"
}

Nereids Planner vs Legacy Planner

Apache Doris提供了两种查询优化器:传统优化器(Legacy Planner)和Nereids Planner。Nereids Planner是新一代的查询优化器,基于Cascades框架实现,具有更强大的优化能力。

如何切换Planner

可以通过会话变量切换使用的Planner:

-- 启用Nereids Planner
SET enable_nereids_planner = true;

-- 启用Legacy Planner
SET enable_nereids_planner = false;

也可以在单个查询中通过HINT指定使用的Planner:

-- 使用Nereids Planner
EXPLAIN SELECT /*+ SET_VAR(enable_nereids_planner=true) */ * FROM test_table;

-- 使用Legacy Planner
EXPLAIN SELECT /*+ SET_VAR(enable_nereids_planner=false) */ * FROM test_table;

示例来源:fe/fe-core/src/test/java/org/apache/doris/policy/PolicyTest.java

两种Planner的执行计划对比

Nereids Planner相比Legacy Planner在执行计划上有以下改进:

  1. 更准确的代价估算:基于统计信息的精确代价模型
  2. 更丰富的算子选择:支持更多类型的连接算法和聚合方式
  3. 更灵活的执行策略:能够根据数据分布动态调整执行计划

以下是使用两种Planner对同一SQL生成的执行计划对比:

Legacy Planner

+--------------------------------------------------+
| ID | OPERATOR        | NAME       | EST. ROWS | ... |
+--------------------------------------------------+
| 0  | EXCHANGE        | GATHER     | 1000      | ... |
| 1  |  AGGREGATE      | SUM        | 1000      | ... |
| 2  |   EXCHANGE      | HASH       | 10000     | ... |
| 3  |    AGGREGATE    | PARTIAL_SUM| 10000     | ... |
| 4  |     SCAN        | OLAP_TABLE | 100000    | ... |
+--------------------------------------------------+

Nereids Planner

+--------------------------------------------------+
| ID | OPERATOR        | NAME       | EST. ROWS | ... |
+--------------------------------------------------+
| 0  | PROJECT         |            | 1000      | ... |
| 1  |  AGGREGATE      | SUM        | 1000      | ... |
| 2  |   SCAN          | OLAP_TABLE | 100000    | ... |
+--------------------------------------------------+

可以看到,Nereids Planner能够识别出该查询可以通过单节点完成,避免了不必要的数据交换,从而提高查询效率。

执行计划分析实战

案例1:识别全表扫描

全表扫描(Full Table Scan)是影响查询性能的常见问题。通过分析执行计划,我们可以快速识别是否存在全表扫描。

以下是一个包含全表扫描的执行计划片段:

| 4  |     SCAN           | OLAP_TABLE | 1000000   | 40000000   | table: test, partitions: [], buckets: [] |

在PROPERTIES中,如果partitions和buckets为空,通常表示发生了全表扫描。解决方法是添加合适的分区过滤条件或创建索引。

案例2:优化JOIN顺序

JOIN顺序对查询性能有很大影响。优化器通常会选择最优的JOIN顺序,但在某些情况下可能需要手动干预。

以下是一个包含多表JOIN的执行计划片段:

+--------------------------------------------------+
| ID | OPERATOR        | NAME       | EST. ROWS | ... |
+--------------------------------------------------+
| 0  | HASH_JOIN       |            | 1000      | ... |
| 1  |  HASH_JOIN      |            | 10000     | ... |
| 2  |   SCAN          | TABLE_A    | 100000    | ... |
| 3  |   SCAN          | TABLE_B    | 10000     | ... |
| 4  |  SCAN           | TABLE_C    | 1000      | ... |
+--------------------------------------------------+

这个执行计划显示先进行TABLE_A和TABLE_B的JOIN,再与TABLE_C进行JOIN。如果TABLE_C的数据量很小,我们可以通过HINT强制先进行TABLE_B和TABLE_C的JOIN,减少中间结果集大小:

EXPLAIN SELECT /*+ JOIN_ORDER(TABLE_B, TABLE_C, TABLE_A) */ ...

案例3:聚合优化

对于大数据量的聚合查询,部分聚合(Partial Aggregation)可以显著提高性能。通过执行计划,我们可以检查是否启用了部分聚合。

以下是一个包含部分聚合的执行计划片段:

+--------------------------------------------------+
| ID | OPERATOR        | NAME       | EST. ROWS | ... |
+--------------------------------------------------+
| 0  | AGGREGATE       | FINAL      | 1000      | ... |
| 1  |  EXCHANGE       | HASH       | 10000     | ... |
| 2  |   AGGREGATE     | PARTIAL    | 10000     | ... |
| 3  |    SCAN         | OLAP_TABLE | 100000    | ... |
+--------------------------------------------------+

如果执行计划中没有PARTIAL_AGGREGATE算子,可能是因为优化器认为部分聚合不会带来性能提升。这时可以通过HINT强制启用部分聚合:

EXPLAIN SELECT /*+ PARTIAL_AGGREGATION() */ ...

高级功能:执行计划可视化

Apache Doris提供了执行计划可视化功能,可以将EXPLAIN输出转换为图形化展示。这对于理解复杂的执行计划非常有帮助。

要使用执行计划可视化功能,可以在EXPLAIN命令后添加FORMAT参数:

EXPLAIN FORMAT=GRAPH SELECT ...;

执行后会生成一个SVG格式的执行计划图,展示算子之间的关系和数据流向。

总结

通过本文的介绍,相信你已经掌握了Apache Doris执行计划的基本概念、结构和分析方法。总结一下关键点:

  1. 使用EXPLAIN命令可以生成SQL语句的执行计划
  2. 执行计划由一系列算子组成,数据从下往上流动
  3. 常见的算子包括SCAN、AGGREGATE、JOIN和EXCHANGE等
  4. Nereids Planner提供了更优的执行计划生成能力
  5. 通过分析执行计划,可以识别和解决查询性能问题

希望本文能帮助你更好地理解和使用Apache Doris,编写出更高效的SQL查询。如果你想深入了解更多执行计划相关的知识,可以参考以下资源:

祝你在Apache Doris的使用之旅中取得更好的性能和体验!

【免费下载链接】doris Apache Doris is an easy-to-use, high performance and unified analytics database. 【免费下载链接】doris 项目地址: https://gitcode.com/gh_mirrors/dori/doris

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值