Presto 查询引擎内核详解:分布式规划机制

从 Physical Plan 到 Distributed Plan

一、引言:为什么需要分布式规划?

在前面的文章中,我们了解到 Presto 如何完成:

复制代码
SQL --> AST --> Analysis --> LogicalPlan --> PhysicalPlan

优化器最终生成的是一棵 Physical Plan Tree(PlanNode Tree)。例如:

复制代码
           Output
              |
           Exchange
              |
            Join
         /        \
    Scan Orders   Exchange
                     \
                    Scan Customer

从单机执行角度看,这棵树已经描述了:

  • 数据如何读取
  • 算子如何连接
  • 数据如何流动

但是,对于一个 MPP 查询引擎而言,集群中的 Worker 节点无法直接执行整棵计划树。原因在于:

  • 数据需要跨节点重新分布;
  • 不同计算阶段需要分布到不同 Worker;
  • 查询需要拆分为多个独立调度单元;
  • 不同阶段之间需要建立依赖关系。

因此,Presto 需要完成一次关键转换:

复制代码
PhysicalPlan --> Distributed Plan

这个过程就被称之为:

Distributed Planning(分布式规划)

完成这一转换的核心组件是 PlanFragmenter,它负责将 Physical Plan 切分为多个 PlanFragment,并构建后续分布式调度所需的 Distributed Plan。

二、整体认知:Presto 的分布式规划及调度执行模型

在Presto中,一个SQL查询从优化后的物理计划转化为可执行的分布式任务,需要经历几个核心阶段:将物理计划切分为计划片段、规划各个片段的分布式执行特性、为分布式计划构造调度器并实际调度和执行。

在深入细节之前,我们需要先建立一个分层的全局认知。

Presto 是一个以 Pipeline Streaming 执行为核心的 MPP 查询引擎,其默认执行模型强调数据流式传输和算子流水执行。同时,为支持 Exchange Materialization、

CTE Materialization 等高级能力,现代 Presto 引入了 Section 等更高层调度抽象,用于统一描述纯 Streaming 执行和带物化依赖的混合执行模型。

  • 随着物化执行等能力的发展,Presto 的调度执行模型逐渐形成 Section、Stage、Task 等多层抽象,用于描述调度执行的边界、生命周期以及依赖关系;
  • 默认情况下,Stage 之间通过 Remote Streaming Exchange 进行流式数据交换,避免中间结果完全物化。在该模型下,整个 Query 的所有 Stage 通常位于同一个 Section 内,Stage 之间通过 Remote Streaming Exchange 形成流水依赖,而不存在跨 Section 的物化依赖 DAG。
  • 在 Exchange Materialization 或 Cte Materialization 等模式下,查询会根据物化依赖边界被切分为多个 Section,Section 之间可以形成显式 DAG 调度关系。
  • 调度以 Section 作为高层组织单元,以 Stage 作为生命周期管理和依赖调度实体,以 Task / Split 作为执行层实际调度对象。

关于 Section 这一层级概念的引入,有必要再专门介绍一下。Presto 最初的分布式执行模型主要围绕 Stage 和 Streaming Exchange 构建,Stage Tree 足以描述

Producer-Consumer 之间的流水依赖关系。随着 Exchange Materialization 等能力的引入,Presto 开始支持具有持久化边界的阶段化执行模式,查询执行不再始终

表现为单一 Streaming Pipeline,而可能被切分为多个具有独立生命周期的执行区域。因此,Section 作为位于 Stage 之上的执行组织抽象被引入,用于表示一组共享

执行生命周期的 Stage,并描述这些独立执行区域之间基于物化数据形成的 DAG 依赖关系。

三、从 PlanNode 到 SubPlan:PlanFragmenter 的核心职责

3.1 PlanFragmenter 做什么?

在 Presto 中,负责完成分布式规划的核心组件为:PlanFragmenter。PlanFragmenter 不仅仅是一个"切 Stage 的工具",它实际上承担的是分布式执行计划构造(Distributed Plan Construction),包括:

  • 执行 Stage 边界划分
  • 构造 PlanFragment
  • 推导 Fragment 的数据分布属性
  • 连接上下游 Stage 的数据流关系
  • 反向推导并调整数据分布需求,协调 Source 与输出 Partitioning
  • 标记 Grouped Execution 能力

用一句话来描述:

PlanFragmenter 将优化器生成的 Physical Plan Tree 转换为面向分布式执行的 SubPlan Tree。在这个过程中,它不仅根据 Remote Exchange 边界切分 Stage,还负责确定每个

PlanFragment 的分布式执行属性,包括内部数据分布(PartitioningHandle)、输出数据分布(PartitioningScheme)以及相关执行能力。

复制代码
输入:PlanNode Tree
      |  PlanFragmenter
      v
输出:SubPlan Tree

SubPlan 是 Presto 分布式规划阶段生成的逻辑执行结构,它表示一个 Stage 及其依赖的子 Stage 组成的树形执行计划。一个 SubPlan 主要由两部分组成:

复制代码
SubPlan
 +-- PlanFragment
 +-- children(SubPlan)

PlanFragment 描述一个 Stage 的计算语义以及其在分布式执行中的数据交换语义

它包含:

  • fragmentId: 当前 Fragment 的唯一标识;
  • root: 当前 Stage 内部执行的 PlanNode 子树根节点;
  • partitioningHandle: 描述当前 Stage 内部数据分布方式;
  • partitioningScheme: 描述当前 Stage 输出数据的分布方式;
  • variables: 描述当前 Stage 输出的变量集合,即该 Stage 对外暴露的数据 schema;
  • tableScanSchedulingOrder: 描述当前 Stage 中包含的按调度顺序排列的 TableScan 节点列表
  • remoteSourceNodes: 描述当前 Stage 中包含的远程数据源的集合
  • execution properties: 描述该 Stage 的执行属性,例如 grouped execution 等等。

因此:

PlanFragment 不仅描述"这个 Stage 内部执行什么计算",还定义了该 Stage 在分布式执行过程中的数据分布语义、输出接口以及执行能力等等信息。

children 描述当前 Stage 依赖的上游 Stages

children(SubPlan) 表示当前 Stage 所依赖的上游 Stage,这些 Stage 通常对应当前 Fragment 中 RemoteSourceNode 背后的 Producer Stage。

后续阶段中,SqlQueryScheduler 会根据 SubPlan Tree 创建对应的 StageExecution,并按照其中描述的依赖关系划分并推进各个 Section / Stage 的调度执行。

3.2 PlanFragmenter 的整体流程

PlanFragmenter 的整体处理过程可以抽象为:

复制代码
createSubPlans()
        |
        v
FragmentProperties 初始化
        |
        v
Visitor 遍历 PlanNode
        |
        +--------------------------------+
        |                                |
        v                                v
普通 PlanNode 处理              ExchangeNode 处理
        |                                |
        |                 +--------------+--------------+
        |                 |                             |
        |                 v                             v
        |        Streaming Exchange           Materialized Exchange
        |                 |                             |
        |                 v                             v
        v         切断 Fragment 边界          创建物化数据边界(构建临时表写入计划)
   设置分布属性             |                             |
        |                 v                             v
        |       生成 RemoteSourceNode           生成 TableScanNode
        |                 |                             |
        |                 +--------------+--------------+
        |                                |
        |                                v
        |                      生成 PlanFragment
        |                                |
        +------buildRootFragment---------+
                                         v
                                     SubPlan
                                         |
                                         v
                               Grouped Execution 分析
                                         |
                                         v
                               Partitioning 修正
                                         |
                                         v

PlanFragmenter 是 Presto 从单体 Physical Plan 到 Distributed Execution Plan 的关键转换层。它通过识别 Exchange 边界,将连续的计算逻辑转换为由多个 Fragment 组成的 Distributed Plan,并同时确定 Fragment 间的数据交换方式、Partitioning 语义以及执行属性。

四、FragmentProperties:Stage 构建过程中的状态模型

PlanFragmenter 在遍历 PlanNode 时,需要维护当前 Stage 的各种属性。这些状态封装在:FragmentProperties 中。从更加工程实现的角度来说,FragmentProperties

可以看作 Fragmenter Visitor 在遍历 PlanNode 过程中维护的"当前 Stage 构建上下文"。

其核心字段包括:

复制代码
FragmentProperties
    +--List<SubPlan> children;
    +--PartitioningScheme partitioningScheme;
    +--Optional<PartitioningHandle> partitioningHandle;
    +--Set<PlanNodeId> partitionedSources;

各个字段的说明如下:

字段 含义
children 遍历过程中推导出来的当前 Stage 依赖的子 Stage 列表
partitioningHandle 遍历过程中推导出来的当前 Stage 内部数据分布方式
partitioningScheme 切分新 Stage 时传递来的当前 Stage 输出数据分布方式
partitionedSources 遍历过程中推导出来的当前 Stage 内的数据源

其中最容易混淆的是:partitioningHandle 和 partitioningScheme 两个概念。

4.1 PartitioningHandle

PartitioningHandle 描述了当前 Stage 内部计算应该采用什么数据布局。

其作用为,决定:

  • Task 如何划分数据
  • Source 如何提供数据

Connector、Exchange 以及执行调度阶段都会基于该分布语义进行数据布局和调度决策。

例如:

复制代码
Hive Bucket(customer_id, 8)

表示数据在源端已按 Hive 桶算法在 customer_id 上分为 8 组,Stage 内部要求沿用此布局进行计算。

4.1 PartitioningHandle

PartitioningHandle 描述当前 Stage 所对应的数据分布(Partitioning)语义。

它并不是一个被直接指定的决定 Stage 数据布局的策略对象,而是 Distributed Planning 过程中,结合 Stage 内各个 PlanNode 的数据分布特性推导出的最终分布语义。

在遍历一个 Stage 所包含的 PlanNode 时,Presto 会结合不同节点的数据分布信息,例如:

  • TableScan 所对应 Connector 提供的源端 Partitioning;
  • RemoteExchange 所定义的数据交换及 Partitioning 语义;
  • 其他 PlanNode 对上游 Partitioning 的保持、转换或破坏;

逐步推导当前 Stage 的 Partitioning,最终形成该 Stage 对应的 PartitioningHandle。

因此,PartitioningHandle 的核心作用是描述"当前 Stage 的数据应该如何被理解和组织"。后续的分布式规划、Connector 数据访问以及 Exchange 和调度过程,可以基于这一 Partitioning 语义决定数据如何分布以及 Task 如何承载这些数据。

例如:

复制代码
Hive Bucket(customer_id, 8)

表示当前 Stage 的数据分布语义为:数据按照 Hive Bucket 规则基于 customer_id 被划分为 8 个 Bucket。该语义可以被后续的 Source、Exchange 以及调度逻辑用于判断数据的分布方式和计算并行度。

4.2 PartitioningScheme

PartitioningScheme 描述当前 Fragment 输出数据时所采用的 Partitioning 方式以及输出布局信息。

与 PartitioningHandle 不同,PartitioningScheme 并不是在遍历当前 Stage 内部 PlanNode 的过程中,根据各节点的分布特性独立推导出来的。它与

Remote Exchange 所定义的数据交换语义直接相关。

在进行分布式计划切分时,当 PlanFragmenter 跨越一个 Remote Exchange 创建新的 Fragment / Stage 时,会根据该 Exchange 定义的 PartitioningScheme

初始化新的 FragmentProperties。因此,PartitioningScheme 本质上是沿着 Exchange 边界传递到新 Fragment 的数据布局描述,并在后续的 Fragment 构建过程中

继续参与分布式规划。

例如:

复制代码
HASH(order_id, 4)

表示该 Exchange 输出的数据按照 order_id 进行 Hash Partitioning,并划分为 4 个分区。下游 Fragment 将基于这一 Partitioning Scheme 接收和处理

来自该 Exchange 的数据。

两者更准确的区别可以理解为:

  • PartitioningHandle:描述"当前 Fragment 的数据分布语义是什么"
  • PartitioningScheme:描述"跨越 Exchange 后,数据以什么分区方式和布局传递给下游 Fragment"

因此,PartitioningScheme 与 PartitioningHandle 虽然并不是严格的一一对应关系,但二者存在明确的数据分布语义传递关系:

复制代码
上游 Stage
    │
    │ PartitioningScheme
    ▼
Remote Exchange
    │
    ▼
RemoteSource
    │
    │ 参与下游 Stage 的 Partitioning 推导
    ▼
下游 Stage
    │
    │ PartitioningHandle
    ▼
当前 Stage 的 Partitioning 

也就是说,上游 Stage 的 PartitioningScheme 描述其通过 Remote Exchange 输出数据时所采用的分区方式;当该 Exchange 在下游 Stage 中表现为 RemoteSource 时,

这一分布信息会成为下游 Fragment Properties 推导 Partitioning 的输入之一,并最终参与形成下游 Stage 的 PartitioningHandle。

五、Exchange:Stage 切分的核心机制

Presto 分布式规划阶段最重要的概念为:

Exchange 决定 Stage 边界。

需要说明的是,"Exchange 决定 Stage 边界"描述的是分布式规划阶段的职责------即根据已经存在的 Remote Exchange 节点进行 Stage 切分。

而在更早的 AddExchanges 优化阶段,优化器会基于不同算子的语义特征决定是否需要以及在何处插入 Remote Exchange 节点。

换言之:

  • AddExchanges 阶段:根据算子特点决定要不要加 Exchange(原因层)
  • 分布式规划阶段:根据已有的 Exchange 执行 Stage 切分(结果层)

本文主要聚焦于后者。理解了这一分层,就不难理解为什么会有如下结论:

不是:

复制代码
Join = Stage
Aggregation = Stage

而是:

复制代码
Remote Exchange = Stage Boundary

Stage 的边界由数据交换节点定义,而交换节点的插入则由上游算子的计算需求驱动。

5.1 Local Exchange

Local Exchange 表示同一个 Stage 内的数据交换(也即是在同一个 Worker 节点上的同一个 Task 内,不同 Pipeline 之间的数据交换)。

例如在一个 Task 内部:

复制代码
Driver A
   |
Local Exchange
   |
Driver B

不会产生新的 Stage。详细机制请参见另一篇文章《Presto 查询引擎内核详解:AddLocalExchanges------基于数据流属性约束的本地执行规划》。

5.2 Remote Streaming Exchange

Remote Streaming Exchange 表示跨 Worker 的流式数据交换。

例如,原始的物理执行计划为:

复制代码
           Output
              |
           Exchange
              |
            Join
         /        \
    Scan Orders   Exchange
                     \
                    Scan Customer

Stage 切分转换之后:

复制代码
   Stage 0              Stage 1                 Stage2

   Output                Join                  Scan Customer
     |                /         \
RemoteSource     Scan Orders  RemoteSource

在 Stage 切分过程中,Exchange 被替换为:RemoteSourceNode。同时,创建新的:Child SubPlan。最终形成如下形状的一个 Stage Tree:

复制代码
    Stage0
      |
    Stage1
      |
    Stage2

六、Materialized Exchange 与 Section

前文提到的 Streaming Exchange 和 Stage 切分,构成了经典的 Stage Tree 执行模型。但当引入物化边界后,调度执行模型可以从树演进为有向无环图(DAG),这便是引入 Section 这一层级的核心价值所在。

6.1 从 Streaming 到 Materialized:物化依赖的引入

Streaming Exchange 的语义是流水线式的:

复制代码
Producer Stage
    |(Network Stream,不落盘)
    ▼
Consumer Stage

Producer 和 Consumer 之间通过网络直接传输数据,不存在中间状态持久化。

Materialized Exchange 则引入了物化依赖:

复制代码
Producer Stage
    |(写入临时表,落盘)
    ▼
Temporary Table
    |(从临时表读取)
    ▼
Consumer Stage

此时 Producer Stage 必须完全执行完毕并物化结果后,Consumer Stage 才能开始执行。这种先写后读的依赖关系,在物化交换边界上引入了同步 barrier,使 Producer 和 Consumer 生命周期解耦,但也牺牲了一部分端到端流水执行能力。

6.2 Section:物化边界定义的执行切分

Materialized Exchange 的出现,在 Stage Tree 中切分出了Section:

复制代码
     Stage Tree                          Section DAG
┌─────────────────┐                ┌─────────────────┐
│    Stage 0, 1   │                │   Section A     │
│       │         │                │  (Stages 0-1)   │
│       ▼         │                └────────┬────────┘
│  Materialized   │                         │ (物化依赖)
│   Exchange      │                         ▼ 
│       │         │                ┌─────────────────┐
│       ▼         │                │   Section B     │
│    Stage 2, 3   │                │  (Stages 2-3)   │
└─────────────────┘                └─────────────────┘

Section 的定义:

Section 是由 Materialized Exchange 边界划分出的调度执行组织单元。同一个 Section 内的 Stage 通过 Streaming Exchange 连接,形成流水线;不同 Section 之间通过 Materialized Exchange 连接,形成物化依赖。

相比早期 Presto 的单纯流式 Stage Tree 模型,Section DAG 带来了几个关键能力:

  • 容错恢复:Materialized Exchange 物化的中间结果可作为检查点,某个 Section 失败后无需重跑整个查询,只需重跑该 Section 及其下游;此外,结合分组执行(grouped execution),可以支持分组级(任务级)的失败重试。
  • 运行时自适应优化:下游 Section 的计划可以基于上游 Section 物化后的真实数据统计信息(如行数、数据分布、NDV、Null 比例等)进行动态调整,引入类似于 Spark AQE 的能力。
  • 资源解耦:不同 Section 可独立调度,无需同时持有所有 Stage 的资源,对大规模 ETL 场景尤其友好。

这一演进扩展了 Presto 的执行模型,使其在保持交互式查询能力的同时,具备更强的长链路查询、大规模数据处理以及复杂工作负载支持能力。

七、Fragment 创建与 SubPlan 组装

在 PlanFragmenter 遍历 PlanNode 的过程中,随着发现不同类型的 Exchange 边界,会逐步完成 Fragment 切分和创建。

因此,PlanFragment 并不是在整个 Plan 遍历完成后统一生成,而是在 Fragment 边界识别过程中逐步构建:

复制代码
Visitor 遍历 PlanNode
        |
        v
发现 Exchange 边界
        |
        v
递归构建 Exchange Source 对应的 Child Fragment
        |
        v
当前 Fragment 替换 Exchange 为 RemoteSourceNode
  或建立物化读取节点
        |
        v
继续处理父节点
        |
        v
构建 Root Fragment
        |
        v
组装 SubPlan Tree

在 Fragment / SubPlan 构建阶段,主要完成以下工作:

1. 确定 Fragment 的执行结构

PlanFragmenter 根据当前 Stage 内部的 PlanNode 拓扑关系,确定:

  • 当前 Fragment 的根节点;
  • Source 节点关系;
  • 数据交换关系。

确保该 Fragment 描述的是一个完整、可执行的计算区域。

2. 汇总执行属性

在前面的遍历过程中,FragmentProperties 已经逐步收集了当前 Stage 的执行信息,包括:

  • 数据分布方式(PartitioningHandle);
  • 输出分区方案(PartitioningScheme);
  • 直属子 Fragment 对应的 SubPlan 依赖关系(children);
  • 相关执行能力属性。

这些信息最终都会被封装到 PlanFragment / SubPlan 中。

3. SubPlan Tree 组装

当一个 Fragment 构建完成后,PlanFragmenter 会根据 FragmentProperties 中维护的子 Fragment 关系构造对应的 SubPlan:

复制代码
        Parent SubPlan
              |
        ----------------
        |              |
Child SubPlan  Child SubPlan
                       |
                     ......

其中:

  • Parent Fragment 通过 RemoteSource 或物化读取节点消费 Child Fragment 输出;
  • Child Fragment 是 Producer Stage;
  • SubPlan Tree 描述 Fragment 之间的数据依赖关系。

至此,PlanFragmenter 完成了 Distributed Plan 的初步构建。生成的 SubPlan Tree 作为后续分布式执行优化和调度规划的基础,接下来的阶段还将进一步分析 Fragment 的执行能力,并调整其数据分布策略。

八、Grouped Execution 能力分析

除了构建 Stage 拓扑和确定数据分布之外,PlanFragmenter 还会对部分执行能力进行分析,其中包括 Grouped Execution。

Grouped Execution 主要用于降低大规模 Join 或 Aggregation 场景下的内存压力。

传统执行模式下,一个 Task 通常需要一次性加载完整的数据分组:

复制代码
   Task
    |
    v
   读取全部 Bucket
    |
    v
   Build Hash Table
    |
    v
   执行 Join

当 Build Side 数据规模较大时,会导致较高的峰值内存消耗。

Grouped Execution 则将执行过程拆分为多个独立的数据组(通常对应 Bucket 或 Lifespan):

复制代码
   Bucket n
    |
    v
   执行 Join
    |
    v
   释放资源

通过限制单次参与计算的数据规模,降低 Hash Build 等算子的内存压力。

在分布式规划阶段,PlanFragmenter 并不负责执行 Grouped Execution,而是通过 GroupedExecutionTagger 分析当前 Fragment 是否具备该能力,并将分析结果记录到:

StageExecutionDescriptor 中以供后续 Stage 调度和 Task 执行阶段使用。

其核心关注点包括:

  • 当前 PlanNode 是否支持 Grouped Execution;
  • 子树是否适合采用 Grouped Execution;
  • 哪些 TableScan 节点可以提供分组数据;
  • 分组数量等执行信息。

关于 Grouped Execution 的完整机制,我们将在后续专门的文章中单独展开。

九、计算需求反向影响数据布局:Partitioning Reassignment

在分布式规划过程中,Presto 不仅需要决定计算如何分布执行,有时还需要根据计算阶段的需求反向调整数据源提供的数据布局。

这一机制称为:Partitioning Reassignment

它体现了 Presto 分布式规划中的一个重要思想:

执行计划中的数据分布需求,可以反向影响 Source 层的数据读取方式。

9.1 为什么需要 Partitioning Reassignment?

在默认情况下,TableScan 通常只提供数据源自身的数据分布方式。

例如,Hive Connector 的某一个表上已经存在的数据分布格式为:

复制代码
bucket(customer_id, 16)

但是,在包含多个 Source 的 Stage 中,Stage 最终需要形成统一的数据分布,而各个 Source 所提供的数据布局未必完全一致。

例如:

复制代码
Stage Requirement

Hive Bucket Partitioning

bucket(customer_id, 8)

这意味着:

数据需要以 Hive 的 Bucket Hash 算法按照 customer_id 的值被划分到 8 个 bucket 中,而不是该表原始划分的 16 个 bucket 中。

如果 Source 提供的数据布局无法满足该要求,则可能导致:

  • 额外的数据 Shuffle;
  • 增加 Network Exchange 开销;
  • 降低 Join 等算子的执行效率。

因此,Presto 会尝试让数据源提供与 Stage 计算需求更加匹配的数据布局。

9.2 计算需求如何影响 Source?

Partitioning Reassignment 的核心过程可以抽象为:

复制代码
Stage Execution Requirement
          |
          v
PartitioningHandle Reassignment
          |
          v
Table Layout 调整
          |
          v
Connector 提供匹配的数据分布

也就是说,分布式规划阶段首先遍历整个计划树,切分 Stage 并自底向上的确定各个 Stage 希望数据以什么方式组织。

然后再自顶向下的将数据输出需求传递给 Source 层,使 Connector 最终调整并提供满足条件的数据布局。

例如:

复制代码
Stage:
HASH(customer_id, 8)
        ↓
TableScan:
Hive Bucket(customer_id, 8)

需要说明的是,在切分阶段(甚至是更早的 AddExchanges 优化阶段)已经确保了一个 Stage 与其内部持有的 Sources 之间的数据布局的兼容性。在优化阶段,优化器已经通过

ConnectorMetadata.getCommonPartitioningHandle 等接口询问并确认过 Source Connector 可以调整并提供兼容的数据布局。否则会通过添加 Remote Exchange

节点来调整数据布局(因此也会将其切分到不同的 Stage 中)。因此,在 Partitioning Reassignment 阶段,不会出现 Stage 要求的数据布局无法被 Source Connector

满足而导致的规划失败。

9.3 体现的数据计算协同思想

传统查询优化通常是:

复制代码
Data Source
    |
    v
Execution Plan

即:数据源提供数据,执行计划适配数据。

而 Partitioning Reassignment 体现的是另一种方向:

复制代码
Execution Requirement
      |
      v
Data Layout Selection
      |
      v
Source Optimization

这是现代湖仓查询引擎中的重要设计趋势:

  • 查询执行不再只是被动消费数据;
  • 执行计划可以影响数据读取方式;
  • 存储布局与计算模型形成协同优化。

在 Presto 中,这种机制连接了:

复制代码
Optimizer
    ↓
Distributed Planning
    ↓
Connector Layer
    ↓
Physical Data Layout

使查询引擎能够根据执行目标选择更加高效的数据访问路径。这体现了一种:"计算分布需求反向约束数据源布局"的设计思想。

十、从 SubPlan 到 Distributed Scheduler

分布式规划阶段生成的 SubPlan 是一种具有 Stage 拓扑、数据分布策略和执行能力描述的 Distributed Plan,其为后续 Query Scheduler 创建 Task、分配 Split 和驱动 Worker 执行奠定基础。

复制代码
Optimizer
    |
    v
PlanNode Tree
    |
    v
PlanFragmenter
    |
    v
SubPlan Tree
    |
    v
SqlQueryScheduler
    |
    v
StageExecution

关于 Query Scheduler 分布式调度部分,将在后续的文章中专门讲解说明,此处不再展开。

总结:Presto 分布式规划的核心思想

Presto 的分布式规划过程,本质上是将优化后的 Physical Plan 转换为包含执行边界、数据交换语义和调度拓扑的 Distributed Execution Model。

这一过程围绕三个核心抽象展开:

1. 通过 Exchange 定义计算边界

Presto 并不是简单按照算子类型切分执行阶段,而是通过识别 Exchange 边界,将连续的计算逻辑划分为多个独立执行区域。

  • Remote Streaming Exchange 定义流式数据交换边界;
  • Materialized Exchange 定义物化数据交换边界。

基于这些边界,PlanFragmenter 将 Physical Plan 切分为多个相互关联的 PlanFragment。

2. 通过 Partitioning 描述分布式数据语义

在 Fragment 切分之后,Presto 需要进一步确定:

  • 数据如何在 Worker 之间分布;
  • Fragment 之间如何交换数据;
  • 下游计算如何消费上游结果。

其中:

  • PartitioningHandle 描述 Fragment 数据分布策略;
  • PartitioningScheme 描述 Fragment 输出数据的分区方案以及数据交换布局。

它们共同定义了分布式执行中的数据流转语义。

3. 通过分层执行模型连接规划与调度

基于 Fragment 依赖关系,Presto 构建 SubPlan,并进一步将其映射为运行时执行模型:

  • Streaming Exchange 保留 Pipeline Streaming 的低延迟执行能力;
  • Materialized Exchange 和 Section 引入持久化执行边界,使查询能够形成更复杂的执行拓扑;
  • Scheduler 根据 SubPlan 管理 Stage 生命周期,并将计算任务调度到 Worker 执行。

最终,Presto 分布式规划的核心目标,是在保留 Pipeline Streaming 低延迟执行特性的同时,将优化后的计算计划转换为具有明确执行边界、数据交换语义和调度拓扑的分布式执行模型,从而连接查询优化与集群调度执行。


作者:王冬

PrestoDB Committer | Presto Iceberg Code Owner

本文章同步发表于:

https://hantangwangd.github.io/zh/posts/2026-08-10-distributed-planning-mechanism.html

相关推荐
heimeiyingwang7 天前
【CI/CD·Jenkins篇】Pipeline 基础:Declarative 与 Scripted 语法详解
ci/cd·pipeline·jenkins
不动明王19847 天前
Presto 查询引擎内核详解:整体架构与查询执行流程
架构·presto·分布式调度·pipeline执行·计算引擎内核
不动明王19849 天前
Presto 查询引擎内核详解:基于多级反馈队列思想的 Worker 调度模型
presto·调度执行·多级反馈队列·交互式查询引擎
不动明王198415 天前
Presto 查询引擎内核详解:Worker 本地执行模型——从物理计划到可调度执行单元
大数据·presto·湖仓·查询引擎内核·pipeline执行
james的分享16 天前
分布式 SQL 查询引擎之Trino
ai·mpp·trino·lakehouse
不动明王198419 天前
Presto 查询引擎内核详解:集群资源管理机制解析
内存管理·计算引擎·presto·revoking·memorypool
不动明王198420 天前
能力联邦:Presto 的一种可能演进方向
大数据·iceberg·presto·湖仓·lance·联邦查询引擎
阿坤带你走近大数据2 个月前
GREENPLUM的介绍
数据仓库·postgresql·pgsql·mpp