☰
Apache Doris 4.0.8 查询执行源码(第 4 篇):查询快不靠 MPP 三个字,真正干活的是这条执行链
2026/10/2 8:50:47 网站建设 项目流程

三台 BE 跑一条 Join,比单机还慢。继续加 BE 后,CPU 没有吃满,网络却先打满。MPP 没失效,它只是忠实地把一个错误的数据搬运方案并行执行了。

Doris 的性能来自优化器选对数据位置、Fragment 控制跨机边界、Pipeline 填满单机核心。MPP 只表示可以并行,不保证并行有收益。

一条 SQL 会经历三次完全不同的拆分

SELECTd.region,SUM(f.pay_amount)FROMfact_order fJOINdim_shop dONf.shop_id=d.shop_idWHEREf.pay_date='2026-08-27'GROUPBYd.region;

执行链不是 SQL 直接广播到所有 BE:

SQL → Nereids 选择扫描、Join 顺序和数据分布 → FE 在 Shuffle 边界切成 PlanFragment → Coordinator 把 Fragment 实例调度到 BE → BE 把 Fragment 拆成 Pipeline DAG → PipelineTask 在有界线程池中执行 → Exchange 传输中间 Block → Coordinator 汇总结果

MPP 官方文档 和 Pipeline 官方文档 分别覆盖跨 BE 和单 BE 两层。把它们混成 MPP 会漏掉性能最关键的边界。

MySQL、Spark 与 Doris 的差异不在会不会 Join

固定同一条件:十亿行事实表关联百万行维表,按地区聚合,结果用于交互式看板。

引擎执行重心优势主要边界
MySQL单实例索引、Nested Loop 与行式执行高选择性点查和事务大范围扫描与并行分析受单机限制
Spark SQLStage、Task 与 Shuffle大批量容错计算、弹性资源调度和物化开销不适合高频交互
Doris常驻 BE、Fragment、向量化 Pipeline低调度开销的并行分析与查询服务错误 Shuffle、倾斜和估算仍会拖垮查询

Doris 的优势是常驻执行引擎把分析查询拆成列式 Block,在 BE 内继续流水并行;代价是需要正确统计信息、数据分布和内存治理。

Fragment 决定数据什么时候跨机器

物理计划遇到跨节点数据交换时插入DataStreamSink与ExchangeNode,并在这里切开 Fragment。小维表可能 Broadcast 到每个事实表节点;两张大表通常按 Join Key Shuffle。

Broadcast dim_shop 全量 → BE1、BE2、BE3 fact_order 本地扫描 → 本地 Join Shuffle fact_order 按 shop_id 重分布 ↘ 对齐后 Join dim_shop 按 shop_id 重分布 ↗

Broadcast 减少事实表搬运,却把维表复制到每个实例;Shuffle 能处理大表,却产生网络、序列化和接收端内存。选错策略时,增加节点会增加复制份数或交换连接,并不必然更快。

Pipeline 解决的是等待,不是消灭阻塞

Join Build、Aggregation 和 Sort 需要阶段性物化,无法组成一条永不停止的流水线。Pipeline 把阻塞算子拆成 Sink 与 Source,再用 Dependency 连接:

Pipeline 0:Scan dim → JoinBuildSink ↓ hash table ready Pipeline 1:Scan fact → JoinProbe → AggSink ↓ aggregation ready Pipeline 2:AggSource → ResultSink

等待 Hash Table 时,PipelineTask 释放执行线程,而不是让一个 OS 线程空等。线程数受 BE 线程池约束,所以并发查询增加时不会按 Fragment 数无限膨胀。

但 Build 侧过大仍会吃内存,热点 Key 仍会造成单实例长尾,Sort 仍可能 Spill。Pipeline 改善调度,不改变算法复杂度。

用 EXPLAIN 和 Profile 分别证明两件事

EXPLAIN回答计划准备怎么做:分区裁剪、Join 类型、数据分布和 Fragment 边界。Query Profile 回答实际上做了多少:扫描行数、Exchange 字节、各实例耗时和峰值内存。

最小验证顺序:

EXPLAINVERBOSESELECTd.region,SUM(f.pay_amount)FROMfact_order fJOINdim_shop dONf.shop_id=d.shop_idWHEREf.pay_date='2026-08-27'GROUPBYd.region;SETenable_profile=true;-- 执行目标 SQLSHOWQUERY PROFILE;

判断只看四个差值:

证据正常含义异常含义
估算行数 vs 实际行数CBO 基数可靠统计信息失真,Join 策略可能错误
各实例 ScanRows数据分布均匀Tablet 或谓词倾斜
ExchangeBytes只搬必要数据Shuffle 放大或裁剪失败
最大实例耗时 vs 中位数并行任务接近单个长尾决定总耗时

这个实验的淘汰条件很明确:增加 BE 后 ExchangeBytes 与长尾实例继续上升,却没有减少有效扫描和总耗时,说明当前拆分方式没有获得 MPP 收益。

源码只追四个入口

在 Apache Doris4.0.8中,源码阅读的最短路径是:

  • FENereidsPlanner:逻辑与物理计划入口;
  • FECoordinator:Fragment 实例调度和结果协调;
  • BE Pipeline Fragment Context:把 Fragment 建成 Pipeline DAG;
  • BE Task Scheduler:在有界线程池中推进 PipelineTask。

阅读时围绕一条 SQL 对照EXPLAIN,定位哪个规则选择了 Join 分布、哪个 Exchange 切开 Fragment、哪个 Dependency 阻塞 Probe。脱离具体计划浏览类文件,只会重新变成架构名词。

面试里真正有区分度的回答

Doris 查询快不是单靠 MPP。Nereids 先决定 Join 顺序和数据分布,FE 在 Shuffle 边界形成 Fragment,Coordinator 将实例放到持有 Tablet 的 BE,BE 再用 PipelineTask 在有界线程池中推进向量化算子。性能最终由扫描裁剪、Exchange 数据量、实例倾斜和阻塞算子内存共同决定。

并行只能缩短被正确拆分的工作;拆错以后,MPP 只是让更多机器一起搬错数据。

官方资料

  • MPP Architecture
  • Pipeline Execution Engine
  • Query Profile
  • Apache Doris 4.0.8 Source

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询