解读执行计划

最近更新时间: 2024-06-12 15:06:00

使用EXPLAIN命令可以查看TDSQL PostgreSQL优化器为每个查询生成的具体执行计划。EXPLAIN的输出是一个节点树。 其中每一行对应一个数据库执行算子, 显示算子的节点类型和优化器为执行这个节点预估的开销。
通过以下示例来解读执行计划

---涉及到两个表的DDL
create table t3(f1 int, f2 int) distribute by hash(f1);
create table t4(f1 int, f2 int) distrinute by hash(f1);

说明:

distribute by hash 表示对t3,t4数据按f1 哈希值进行分布到不同节点dn上。

查看关联SQL的执行计划explain select * from t3, t4 where t3.f1=t4.f2;可以得到如下输出结果

                                QUERY PLAN
--------------------------------------------------------------------------------
Remote Subquery Scan on all (datanode_1,datanode_2) (cost=120.19..222.56 rows=4556  width=16)
  -> Hash Join (cost=120.19..222.56 rows=4556 width=16) 
      Hash Cond:(t4.f2 = t3.f1)
      -> Remote Subquery Scan on all (datanode_1,datanode_2) (cost=100.00..120.53  rows=675 width=8) 
          Distribute results by H: f2 
          -> Seq Scan on t4 (cost=0.00..11.75 rows=675 width=8)
      -> Hash (cost=11.75..11.75 rows=675 width=8) 
          -> Seq Scan on t3 (cost=0.00..11.75 rows=675 width=8)
(8rows)

这个执行计划可以简化成以下执行计划树:

执行计划树里的每个节点代笔一个执行算子(在“执行计划节点类型”中详述)。每个节点的EXPLAIN输出( 如下所示)格式为 算子名称(算子开销预估)。 -> Hash Join(cost=120.19..222.56 rows=4556 width=16) Hash Cond:(t4.f2 = t3.f1) 括号中的数字从左到右依次是:

  • 启动代价: 这是该算子在读取第一条元组前的开销预估。比方说索引扫描算子的启动代价就是读取目标表的索引页, 读取到第一个元组的开销。以上示例中的哈希关联(Hash Join)的启动代价是120.19。

  • 算子总代价:这个改算子从执行到结束的总代价。以上示例中的哈希关联(Hash Join)的总代价是222.56。

  • 算子输出的总行数。 以上示例中的哈希关联(Hash Join)的预估输出行数是4556。

  • 算子输出行的行宽(字节数)。 以上示例中的哈希关联(Hash Join)的预估行宽是16字节。

有些算子在EXPLAIN的输出里除了上述的基本通用信息外, 还有算子的一些特定信息。 比方说上面的哈希关联算子的EXAPLIN输出还打印了关联条件。 在SQL执行计划输出结果中, 通常

  • 最底层节点是表扫描节点, 扫描节点返回表中的原数据行。 不同的表有不同的扫描节点类型:顺序扫描,索引扫描和位图索引扫描。最底层的扫描节点也可能是也有非表列源,如VALUES子句并设置FROM返回,它们有自己的扫描类型。

  • 如果查询需要关联,聚合,排序或其他操作,会在扫描节点之上增加节点执行这些操作。这些操作通常都有多种方法,因此在这些位置也有可能出现不同的执行节点类型。

  • 分布式计划中通常会有个特殊的算子(“Remote Subquery Scan”)。这个算子实现了分布式架构的核心数据shuffle的功能。 这个算子有几种不同的形态, 分别对应分布式架构下不同的数据shuffle 功能:

    (1)数据收集形态 - 作用是CN从DN收集数据。

    (2)数据重分布形态 - 作用是CN根据选定的列按照特定的规则把数据重分布到所有的DN。

        数据重分布的规则有: 
    • Redistribute by Hash - 把指定的列按照hash redistribute。 这种情况下, 执行计划的输出里会在“Remote Subquery Scan”的那一行的下面一行打印 - “Distribute results by H: 列名”。 如下所示。 其中"Remote Subquery Scan on all" 后面括号里显示的是该算子在哪些数据节点上执行。

      -> Remote Subquery Scan on all (datanode_1,datanode_2) (cost=100.00..120.53 rows=675 width=8)

      Distribute results by H: f2

      -> Seq Scan on t4 (cost=0.00..11.75 rows=675 width=8)

    • Redistribute by Shard - 把指定的列按照shard redistribute。 这种情况下, 执行计划的输出里会在“Remote Subquery Scan”的那一行的下面一行打印 - “Distribute results by S: 列名”

    • Broadcast - 把指定的表广播到所有的数据节点上。 这种情况下, 执行计划输出里在“Remote Subquery Scan”的那一行下面没有额外的重分布信息。

  • 在分布式架构中, 当SQL可以完全下推到各个DN上计算,中间计算结果不需要重新分布到其他DN上的时候, 执行计划中会有个分布式快速执行算子 - “Remote Fast Query Execution”, 如下示例。在下面这个例子里,两表关联的列都是分布健。这种情况下,各个DN上各自独立执行SQL,CN只需要把DN返回的结果直接返回即可,无需额外的计算。

     postgres@coord1=# explain select * from t3, t4 where t3.f1=t4.f1;
                          QUERY PLAN
     ------------------------------------------------------------------
     Remote Fast Query Execution (cost=0.00..0.00 rows=0 width=0) 
      Node/s:datanode_1, datanode_2
      ->Merge Join (cost=187.38..330.81 rows=9112 width=16)
        Merge Cond:(t3.f1=t4.f1)
        -> Sort (cost=93.69..97.07 rows=1350 width=8)
           Sort Key: t3.f1
           -> Seq Scan on t3 (cost=0.00..23.50 rows=1350 width=8) 
        -> Sort (cost=93.69..97.07 rows=1350 width=8)
           Sort Key: t4.f1
           -> Seq Scan on t4 (cost=0.00..23.50 rows=1350 width=8)
     (10rows)

    在了解了这些特点之后,我们会对EXPLAIN 命令的输出有一个整体结构的概念。其实EXPLAIN 输出的就是一个用户可视化的查询计划树,可以告诉我们执行了哪些节点(操作),并且每个节点(操作)的代价预估是什么样的。