分布式 OLAP 查询引擎 — 系列规划
本文是写作规划,不是可发布正文。拆解对象分两层:查询优化与执行框架(Calcite / 规则与代价模型)与分布式 OLAP 引擎(Trino 主线,Spark SQL / DuckDB / DataFusion 对照)。不写 SQL 语法教程,不写 BI 工具选型,不写云托管 Presto/EMR Serverless 内部调度与定价。
关联文档:
index.md(读者入口)。本文件聚焦逐章大纲、来源台账、实验台账与写作顺序。续作关系:闭合数据平台栈最后一块——lakehouse 第 18 章 从「引擎如何读湖(下推链路)」切入;本系列从「SQL 如何变成物理计划并在 MPP 上跑起来」切入,第 15 篇回调 lakehouse/18,从 planning 层重述 partition/file/row-group pruning。
一、为什么写这个系列
1.1 数据平台栈的缺口
| 已有内容 | 视角 | 落点 |
|---|---|---|
db/postgresql-kernel/ (26) |
行存 OLTP | 进程内 B-Tree + CBO + 执行器 |
db/mysql-innodb/ (20) |
行存 OLTP | InnoDB + handler/optimizer 边界 |
db/columnar-engine/ (16) |
读优化列存 | MergeTree 向量化、morsel-driven |
db/lakehouse/ (21) |
湖仓表格式 | Parquet/Iceberg;第 18 章仅 1 篇引擎读湖 |
stream-processing/ (18) |
实时计算 | Kafka/Flink;不写交互式 OLAP |
db/lsm-tree/ (5) |
写优化 DIY | 未落到分布式查询 |
distributed/ (69) |
分布式理论 | 未落到 Trino/Spark 工程 |
结论:lakehouse 规划公式 Lakehouse = 列式文件 + 对象存储 + 表格式 + catalog + 查询引擎 中,前五项已有系统拆解;查询引擎仍缺独立系列。读者建完湖、接上 Flink 入湖之后,仍会卡在「Trino 为什么全表扫、CBO 估错 cardinality 后 join 顺序怎么崩、shuffle 倾斜从哪一层看」——lakehouse/18 讲下推漏斗,不讲引擎内部的 IR、优化与 MPP 执行。
1.2 为什么选 Trino(主)+ Spark / DuckDB / DataFusion(对照)
| 维度 | Trino | Spark SQL | DuckDB | DataFusion |
|---|---|---|---|---|
| 角色 | 交互式分布式 OLAP | 批/流统一 + 湖仓默认引擎之一 | 嵌入式分析 | Rust 向量化引擎框架 |
| 规范/源码 | trino 开源、Connector SPI 清晰 |
Catalyst + Tungsten 文档与源码 | 官方 Internals | Apache 项目、Arrow 原生 |
| 与 lakehouse | Iceberg connector 成熟 | Iceberg/Delta 一等公民 | PyIceberg + 本机已有实测 | Iceberg rust 生态 |
| 系列篇幅 | 主线 4 篇(10–12, 15) | 对照 1 篇(13) | 对照 1 篇(14,含 DataFusion) | 并入第 14 篇 |
选 Trino 作主线:引擎中立读湖的事实默认之一、Coordinator/Worker 边界清晰、EXPLAIN ANALYZE 可观测;Spark 代表 JVM 批处理栈与 AQE;DuckDB/DataFusion 代表嵌入式与 Rust 向量化路径,与 columnar-engine DuckDB 篇 对读但不重复 MergeTree 存储层。
1.3 系列定位
分布式 OLAP 查询引擎 = SQL 前端 + 逻辑/物理优化 + 执行模型 + MPP 调度 + Connector 下推
不写:
- SQL 语法大全、窗口函数 cookbook
- Superset / Metabase / Tableau 等 BI 层
- 云厂商 Athena / BigQuery / Redshift 内部实现与定价
- Presto 历史品牌百科(Trino 分叉事实一句带过)
写:
- 一条 SELECT 从 Parser 到 Task 上 Page 流的 IR 层级与职责边界。
- CBO 如何用统计信息估 join cardinality、选 hash vs merge join。
- Volcano pull、向量化 batch、morsel-driven 三种执行语义及与列存的衔接。
- MPP 里 Split、Stage、Exchange、Shuffle 如何切分与倾斜。
- Iceberg connector 的 constraint pushdown 在 Trino 哪一层完成(深化 lakehouse/18)。
二、核心问题与目标读者
2.1 六个关键问题
一条 SQL 从文本到机器上跑的 Task,中间经过哪几层 IR?各自输入输出是什么? → 第 1、2、3 篇:AST → Analyzed → Logical → Optimized → Physical → Distributed plan。
CBO 在 join 顺序、join 算法、分区裁剪上怎么做决策?统计错了会怎样? → 第 4、5 篇:NDV、histogram、join reorder、DPP。
Volcano、向量化批处理、morsel-driven 三种执行模型差在哪? → 第 6、7 篇;与 columnar-engine/04 对照。
MPP 架构里 coordinator、worker、shuffle、广播各保证什么? → 第 10、11 篇:Split 调度、Exchange 实现、stage 边界。
Trino / Spark / DuckDB 读 Iceberg 表时,下推在哪一层完成? → 第 15 篇;与 lakehouse/18 对读,从引擎 planning 视角重述。
生产环境查询慢、OOM、shuffle 倾斜怎么诊断?引擎怎么选? → 第 16、17、18 篇:内存账户、spill、runtime filter、选型决策树。
2.2 目标读者
- 数据平台 / 数据工程师:维护 Trino/Spark 集群或 Serverless 查询层,调湖上 SQL。
- 从 lakehouse / stream-processing 系列过来的读者:已有表格式与入湖直觉,要搞清「谁在读我的 manifest」。
- 后端工程师:要把 OLAP 接到业务,需要理解延迟、并发、资源隔离边界。
- 先修:SQL、基本分布式概念(
distributed/任选日志/复制相关篇)、lakehouse/02 Parquet 或 columnar-engine/01 列存直觉。 - 不假设:读过 Calcite/Trino 源码或调过 Spark AQE。
三、篇目结构(全 18 篇)
第一部分:查询引擎基础(第 1–3 篇)
第 1 篇:OLAP 查询引擎全景
- OLTP vs OLAP vs HTAP:存储耦合、优化目标、延迟口径。
- 嵌入式(DuckDB)vs 分布式(Trino/Spark):何时需要 MPP shuffle。
- 批式扫描 vs 交互式:与 stream-processing/01 的流批边界;物化视图/预聚合一句带过。
- 数据平台栈闭合图:行存 → 列存 → 湖格式 → 流计算 → 本系列。
- 全系列地图:前端 → 优化 → 执行 → MPP → 引擎主线 → 生产。
- 来源:Trino Overview、Spark SQL Guide、DuckDB Architecture(A 级)。
第 2 篇:SQL 解析、分析与逻辑计划
- Parser → AST;Analyzer:name resolution、类型推导、视图展开。
- Catalog 与 Schema:table/column 元数据、connector 提供的 handle。
- LogicalPlan 节点:TableScan、Filter、Project、Join、Aggregate、Sort。
- 与 postgresql-kernel 优化器相关篇 的对照:PG 的 parse/rewrite/plan 边界。
- 来源:Trino
SqlParser/Analyzer、Calcite SQL to Rel;PG 文档 Planner(引用)。A 级。 - 实验:同一条 SQL 在 Trino
EXPLAIN(logical)与 DuckDBEXPLAIN输出对比(标注版本,输出可删减)。
第 3 篇:Calcite 与规则/代价优化框架
- RelNode、Convention、Trait(如
PHYSICAL、DISTRIBUTION)。 - Rule-based:谓词下推、投影裁剪、join 消除等规则链。
- VolcanoPlanner / Cascades 搜索:memo、group、implementation rules。
- Trino 与 Calcite 的关系:Trino 自研 planner 与 Calcium 借鉴点(标注版本,不混为一谈)。
- 来源:Calcite Documentation、Trino Developer guide planner 章节。A 级。
第二部分:统计与物理优化(第 4–5 篇)
第 4 篇:统计信息与代价模型
- Table/column statistics:row count、NDV、null fraction、min/max、histogram。
- 统计来源:ANALYZE、connector 提供的 layout handle、Iceberg Puffin / manifest stats。
- Cost model 组成:CPU、IO、network(shuffle)权重;Trino
CostCalculator边界。 - 统计过期与估算误差:全表扫误触发、join 中间结果爆炸的前兆。
- 来源:Trino Cost-based optimizations、PostgreSQL Planner Stats(对照)。A 级。
- 实验:同表有/无 ANALYZE 时 Trino
EXPLAINestimated rows 对比(Docker Trino + TPCH 小表)。
第 5 篇:Join 重排与物理算子选择
- Join order enumeration:left-deep vs bushy;DP vs
启发式(Trino
JoinOrderOptimizer边界)。 - 物理 join:Nested Loop、Hash Join、Merge Join 适用条件与内存假设。
- Broadcast vs Partitioned join:小表广播阈值、shuffle join 网络代价。
- Dynamic partition pruning(DPP):runtime filter 如何缩小 probe 端 scan。
- 来源:Trino 文档 Join planning、Spark Adaptive Query Execution(B 级对照)。A/B 级。
- 实验:
EXPLAIN ANALYZE对比 broadcast vs shuffle join 计划(标注数据规模,≥3 次取中位数 wall time)。
第三部分:执行模型与物理算子(第 6–9 篇)
第 6 篇:Volcano 迭代器模型
- Open / Next / Close;pull 语义;pipeline breakers(Sort、Hash Aggregate)。
- 表达式求值:constant folding、short-circuit、null handling。
- 与行存 OLTP 执行器(PG
ExecutorRun)的结构对照。 - 来源:Graefe Volcano 论文(经典)、Trino operator 接口。A 级。
第 7 篇:向量化与 morsel-driven 执行
- 列向量 batch:SelectionVector、flat vs dictionary block。
- SIMD 友好布局;与 columnar-engine/04 同一套直觉在不同引擎的落地。
- Morsel-driven 并行:work-stealing、NUMA 感知(DuckDB 主线;Trino Page 流对照)。
- 来源:DuckDB Vectorized Execution、Trino Page 结构、MonetDB/X100 论文(引用)。A 级。
- 实验:DuckDB 同一查询
EXPLAIN ANALYZE的 threads 与 timing(本机已有 DuckDB 环境,复用 lakehouse 口径)。
第 8 篇:Scan、Filter 与 Project
- TableScan → ConnectorPageSource:split 粒度、column pruning、limit pushdown。
- Filter 与 Project 的下推边界:哪些谓词必须留在 scan 之上。
- Lazy materialization、dictionary-encoded page 上的 filter。
- 来源:Trino SPI ConnectorSplit / PageSource。A 级。
第 9 篇:Hash Join 与 Hash Aggregation
- Build/probe 阶段;join hash table 布局;outer join 标记位。
- Aggregation:partial → final 两阶段;DISTINCT 与 GROUP BY 的内存差异。
- Spill 触发:内存账户、revocable memory、磁盘 spill 文件格式(Trino 边界)。
- 来源:Trino Hash join / Aggregation
文档与源码
HashBuilderOperator。A 级。 - 实验:构造大 join 超
query.max-memory,观察 spill 是否出现及EXPLAIN ANALYZEPhysicalInput/Output(标注配置,不伪造未观测指标)。
第四部分:MPP 架构(第 10–11 篇)
第 10 篇:Coordinator 与 Worker 架构
- Client → Coordinator:session、transaction(只读边界)、query 生命周期。
- Query → Stage → Task → Driver → Operator pipeline。
- Split 调度:node scheduler、split 与 data locality(connector 提供的 host affinity)。
- 与 Spark Driver/Executor/Stage 的对照表(点到为止,详见第 13 篇)。
- 来源:Trino Overview Architecture、Spark Job Scheduling。A 级。
第 11 篇:Shuffle 与 Exchange
- Partitioning function:hash、round-robin、broadcast、replicate。
- Exchange operator:LOCAL vs
REMOTE;
PartitionedOutput/MergeExchange。 - 数据倾斜:skewed key 在 plan 上的表现;Adaptive 缓解(Spark AQE 对照,Trino 边界)。
- 网络与压缩:serde、pages 大小、exchange client/server。
- 来源:Trino
ExchangeOperator、Spark Shuffle 文档。A 级。 - 实验:skewed key 表 join,
EXPLAIN ANALYZE各 task raw input rows 分布(Trino UI 或 CLI,输出可删减)。
第五部分:引擎主线与对照(第 12–15 篇)
第 12 篇:Trino 查询路径全拆解(主线)
- 从
SqlQueryExecution到 worker 上 task 启动的时序。 - Split 来源:Iceberg 表 → snapshot → manifest filtering → file split。
- Page 流在 operator 链上的传递;pipeline 并行度与
task.concurrency。 EXPLAIN/EXPLAIN ANALYZE字段怎么读:Layout、Estimates、CPU/ wall time。- 来源:
trino源码(标注 release)、Trino Documentation。A 级。 - 实验:Docker Trino + Iceberg REST + MinIO,对 lakehouse reproduce 栈
复用;跑一条带分区谓词的
SELECT,
EXPLAIN ANALYZE记录 scanned rows vs total rows。
第 13 篇:Spark SQL 与 Catalyst(对照)
- Unresolved → Analyzed → Optimized → Physical plan;Whole-stage codegen 边界。
- AQE:动态 shuffle partition、skew join optimization、coalesce partitions。
- Spark 读 Iceberg:V2 reader、column pruning、runtime filtering(与 Trino 对照表)。
- 来源:Spark SQL Guide、Iceberg Spark 文档。A
级;本机可用
local[*]模式做 lightweight 实验。 - 实验:同一 Iceberg 表 Spark
explainvs TrinoEXPLAIN(计划结构对比,不排名 latency)。
第 14 篇:DuckDB 与 DataFusion(嵌入式对照)
- DuckDB:storage-agnostic 分析引擎;向量化 + parallel pipeline;attach 外部 Parquet/Iceberg。
- DataFusion:Arrow 原生 plan、physical optimizer rule、可嵌入 Rust 服务。
- 何时选嵌入式 vs 分布式:数据量、并发、联邦查询边界。
- 与 columnar-engine/11–13 的分工:那边偏 ClickHouse 存储层,本篇偏「无 server 的查询执行」。
- 来源:DuckDB Docs、DataFusion Concepts。A 级。
- 实验:DuckDB 读 lakehouse 实验生成的 Parquet/Iceberg 表,对比 Trino 同 SQL 的 scanned bytes(本机 DuckDB + Docker Trino,标注环境)。
第 15 篇:Iceberg Connector 下推全链路(与 lakehouse/18 对读)
- 从 引擎 planning 重述四层漏斗:partition pruning → file pruning → row-group → dictionary。
- Trino Iceberg
connector:
IcebergMetadata、manifest scan 下推、$files 系统表边界。 - Spark / DuckDB 同一谓词的下推深度对照(能力表,引用官方文档 + 本系列实验)。
- 明确分工:lakehouse/18 讲「下推链路是什么」;本篇讲「谁在 planner 哪一步发起 layout constraint」。
- 来源:Trino Iceberg connector 源码、Iceberg spec Planning、lakehouse/08/18。A 级。
第六部分:生产与收束(第 16–18 篇)
第 16 篇:内存、Spill 与资源隔离
- Query / user / cluster 内存限额;revocable vs non-revocable memory。
- Spill 路径:join、aggregation、order by 各自的 spill 机制。
- 并发调度:Resource group、query priority、hard concurrency limit。
- 与 stream-processing/18 背压概念对照(交互式 vs 流式)。
- 来源:Trino Memory management / Resource groups。A 级。
第 17 篇:经典故障与排查
- 全表扫:缺统计、分区谓词不可推导、connector 未下推。
- 大 shuffle:join key 高基数、broadcast 误判、AQE 未生效(Spark)。
- OOM 与 spill 失败;straggler task;metastore/catalog 超时。
- 排查工具链:
EXPLAIN ANALYZE、event listener、JMX、Spark UI(B 级截图须标注来源)。 - 来源:Trino Troubleshooting、公开复盘(B 级)。A/B 级。
第 18 篇:引擎选型与数据平台阅读地图
- 决策树:交互式联邦 vs 批处理 ETL vs 嵌入式分析 vs 流批一体(RisingWave 等一句带过,指向 stream-processing/18)。
- 能力对照表(带口径):Trino / Spark / ClickHouse / DuckDB / DataFusion / PG — 下推深度、并发模型、湖格式成熟度、运维复杂度。
- 数据平台全栈阅读顺序:postgresql → columnar → lakehouse → stream-processing → 本系列 → db-frontier 可选。
- 开放问题:Unified analytics engine、substrait、Velox 接入边界(标注 B 级前瞻,不冒充结论)。
四、依赖关系
1 → 2 → 3
3 → 4 → 5
1 → 6 → 7 → 8 → 9
1 → 10 → 11
5,8,10 → 12
12 → 13 → 14 → 15
9,11,12 → 16 → 17 → 18
跨系列:
db/lakehouse/18 ──→ 15(下推对读)
db/lakehouse/08 ──→ 12, 15(manifest 语义)
db/columnar-engine/04 ──→ 7(向量化)
db/postgresql-kernel/10-12 ──→ 2, 4(优化器对照)
stream-processing/01,18 ──→ 1, 16(流批边界)
distributed/ ──→ 10, 11(shuffle 与容错直觉)
五、阅读路径
| 路径 | 篇目 | 适合 |
|---|---|---|
| 数据平台全栈 | 1→2→4→10→12→15→18 | 从湖仓到查询层闭合 |
| 从 lakehouse/18 来 | 2→3→5→12→15 | 搞清 planner 与下推发起层 |
| 从 columnar-engine 来 | 1→6→7→14 | 执行模型与嵌入式对照 |
| Trino 运维 | 1→10→12→16→17 | 集群调优与排查 |
| Spark 读者转 Trino | 1→5→11→12→13→15 | 对照 Catalyst/AQE |
| 优化器理论 | 2→3→4→5→6 | IR 与 CBO |
| 完整通读 | 1→…→18 | 系统掌握 |
六、来源台账
A 级
- Trino Documentation(Overview、SQL、Optimizer、Connectors、Iceberg、Memory、Resource groups)。
trinodb/trino源码(标注 release,如 476+ 或写作时最新 stable)。- Apache Calcite Documentation(SQL parser、Rel、Rules、Cost)。
- Apache Spark SQL Guide、Structured Streaming
边界一句;
spark源码 Catalyst 包(标注 3.5+)。 - DuckDB Documentation(Architecture、Optimizer、Iceberg extension)。
- Apache DataFusion Documentation Concepts / physical optimizer。
- Apache Iceberg Spec(Planning、Metrics、Puffin);Trino/Spark/DuckDB connector 文档。
- Graefe, Volcano — An Extensible and Parallel Query Evaluation System(经典引用)。
- Boncz et al., MonetDB/X100 向量化论文(引用)。
B 级
- Presto/Trino 分叉历史、Velox/Substrait 社区博客(标注日期)。
- 公开事故复盘(coordinator OOM、metastore 雪崩、shuffle 倾斜导致 SLA 违约)。
禁止
- 无版本上下文的「Trino 比 Spark 快 N 倍」营销页单独支撑结论。
- 未实测的 latency/scan 数字写进正文。
- 伪造
EXPLAIN ANALYZE输出或 Web UI 截图。
七、实验台账
post/db/lakehouse/reproduce/);可选
Spark local[*] + Iceberg runtime;本机
DuckDB(与 lakehouse/18 同口径:Python 3.14
+ DuckDB 1.5.x)。逐篇交代 CPU、内存、OS、组件版本。 || 篇 | 实验 | 状态 |
|---|---|---|
| 2 | Trino vs DuckDB EXPLAIN logical
结构对比 |
DuckDB 已跑;Trino 待读者 Docker |
| 4 | 有/无 ANALYZE 的 estimated rows 对比 | Trino 待读者 Docker |
| 5 | broadcast vs shuffle join EXPLAIN ANALYZE
timing |
DuckDB join 中位 0.0028s 已跑 |
| 7 | DuckDB 向量化查询 EXPLAIN ANALYZE
threads/timing |
已跑 |
| 9 | 大 join 触发 spill 的配置与 EXPLAIN ANALYZE
片段 |
文档+配置说明 |
| 11 | skewed key join 各 task input rows 分布 | Trino 待读者 Docker |
| 12 | Trino 读 Iceberg 分区表 scanned vs total rows | Trino 待读者 Docker |
| 13 | Spark vs Trino 同表 explain 计划对照 |
文档引用 |
| 14 | DuckDB vs Trino scanned bytes 对照 | DuckDB 已跑 |
| 15 | 四层谓词各裁一层,记录 plan 中 layout constraint | 复用 lakehouse/18 PyIceberg |
复现脚本目录:reproduce/(docker-compose.yml
扩展 lakehouse 栈、tpch/
小数据集生成、run_explain.sh)。
每个 benchmark:CPU、内存、OS、组件版本、数据规模、采样轮次(≥3,取中位数);Trino/Spark 未在本机跑过的实验按 lakehouse/18 惯例写清限制,给出可复现步骤但不伪造输出。
八、系列联动
| 系列 | 联动 |
|---|---|
db/lakehouse/ |
18 下推链路 ↔︎ 本系列 12/15;08 manifest ↔︎ split 来源 |
db/columnar-engine/ |
04 向量化 ↔︎ 07;DuckDB 11–13 ↔︎ 14 |
db/postgresql-kernel/ |
10–12 优化器/执行器 ↔︎ 02/04/06 |
stream-processing/ |
01 流批边界 ↔︎ 01;18 背压 ↔︎ 16 |
db/lsm-tree/ |
不重复;RocksDB 系列若开写则 16 内存一节交叉引用 |
db-frontier/ |
12 HTAP、23 增量视图 ↔︎ 18 前瞻一句 |
九、边界
不承诺
- 替代 Trino / Spark / DuckDB 官方文档。
- SQL 教程、DBT/Airflow 编排、数据建模。
- ClickHouse 分布式 SQL 全文(已有 columnar-engine 系列;18 篇对照表一句)。
- Hive LLAP / Impala / Dremio 独立成篇(18 篇对照表带过)。
- GPU 加速查询(db-frontier / GPU 系列边界)。
承诺
- 以「SQL → 计划 → MPP 执行 → 湖上下推」内核视角组织。
- 关键结论锚定官方文档、源码或实测。
- 与 lakehouse/18 明确分工,避免重复粘贴下推漏斗全文。
十、写作顺序
index.md(与 PLAN 同步)- 第 1 篇(全景,不依赖 Docker)
- 搭建
reproduce/docker-compose.yml(Trino + 复用 lakehouse MinIO/Iceberg) - 第 2、3 篇(前端与 Calcite 框架)
- 第 10、12 篇(Trino 架构与全路径,确立主线)
- 第 4、5 篇(统计与 join 规划,依赖 Trino 环境)
- 第 6、7、8、9 篇(执行模型与算子)
- 第 11 篇(shuffle)
- 第 15 篇(Iceberg 下推,回调 lakehouse/18)
- 第 13、14 篇(Spark / DuckDB 对照)
- 第 16、17、18 篇(生产与收束)
- 更新
post/db/index.md、post/series/index.md
十一、已确认决策(2026-07-05)
- 系列规模:18 篇(与 stream-processing 对齐;比 lakehouse 精简)。
- 版本锚定:Trino 476+(或写作时 latest stable);Spark 3.5+;DuckDB 1.5.x;Iceberg 1.x table spec V2 主线。
- 引擎主线:Trino 分布式路径;Spark/DuckDB 各 1 篇对照 + 18 篇选型表。
- lakehouse/18 边界:18 章保留「读湖漏斗」独立可读;本系列 15 从 planner 视角深化,不迁移 18 章正文。
- 实验栈:优先扩展 lakehouse
reproduce/,不另起一套 MinIO 数据。
十二、待确认问题
- 系列目录:
post/db/query-engine/(已定)vs 顶层post/query-engine/?——建议post/db/query-engine/,与 lakehouse/columnar 并列。 - Trino 是否固定 single-coordinator + 2 worker,还是 single-node 开发模式足够?
- TPCH 数据规模:sf0.01 够 explain 实验,sf1 用于 timing——是否统一 sf0.1?
- Spark 篇深度:仅
local[*]+ Iceberg,还是 Docker spark-submit cluster?
规划版本:v1,2026-07-05
目录:post/db/query-engine/
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【分布式 OLAP 查询引擎】Trino · Spark · DuckDB · 优化与 MPP 执行
闭合数据平台栈最后一块:从 SQL 解析与 Calcite 式优化,到 Volcano/向量化执行、Trino Coordinator/Worker 与 shuffle,再到 Iceberg connector 下推与生产排查。承接 lakehouse 第 18 章读湖视角,补全「谁在做 planning」的引擎内核层。