土法炼钢兴趣小组的算法知识备份

【分布式 OLAP 查询引擎】DuckDB 与 DataFusion:嵌入式分析对照

文章导航

分类入口
databasedistributed
标签入口
#duckdb#datafusion#embedded-olap#vectorized-execution#morsel-driven#parquet#iceberg#trino#query-engine

目录

第 12 篇 把 Trino 的 Query → Stage → Task → Page 流拆到底:Coordinator 做分布式 planning,Worker 之间用 Exchange 传 Page。很多读者会问:笔记本上 DuckDB 一条 SQL 也能 join 百万行、读 Parquet/Iceberg——它和 Trino 差在哪?Rust 写的 DataFusion 又扮演什么角色?

本文是系列 第 14 篇,回答 嵌入式 OLAP分布式 MPP 的分工:

实测环境:本机 WSL2(Linux 6.6.87.2)、i9-12900K / 32 GiB。DuckDB 1.5.4(Python 绑定 + CLI)为本机真实执行;未部署 Trino/Spark(无 JVM 查询集群)。Trino 行为按官方文档引用,不粘贴未实测的 EXPLAIN ANALYZE 数字。复现脚本:reproduce/run_duckdb_experiments.py


一、嵌入式 vs 分布式:分界不在「能不能读 Parquet」

第 1 篇 已给出分界表,本篇从 执行拓扑 收紧结论:

维度 DuckDB / DataFusion(嵌入式) Trino / Spark(分布式)
进程模型 嵌入宿主或 CLI 单进程 Coordinator/Driver + 多 Worker/Executor
并行 多线程 pipeline,共享地址空间 跨节点 Task + 网络 shuffle
联邦 ATTACH / 扩展读外部 catalog,规模受单机内存与线程限制 Connector SPI,跨源 join 是设计目标
并发租户 单进程内串行或有限并行,无远程 SQL 服务语义 多 session、resource group、队列
读湖 planning 单进程内 metadata + scan(lakehouse/18 实验二) Coordinator 集中 planning,split 分发

工程判断:数据与并发能放进 一台机器、一个进程 的内存与磁盘预算 → DuckDB 往往足够;需要 多用户交互式联邦、PB 级数据并行扫、跨机 shuffle → 上 Trino/Spark(第 18 篇 决策树)。

flowchart LR
  subgraph emb ["Embedded"]
    APP["Notebook / Service"]
    DDB["DuckDB / DataFusion"]
    FS["Parquet / Iceberg files"]
    APP --> DDB --> FS
  end
  subgraph mpp ["Distributed MPP"]
    CLI["JDBC / CLI"]
    COORD["Coordinator"]
    W1["Worker"]
    W2["Worker"]
    LAKE["Object storage"]
    CLI --> COORD --> W1
    COORD --> W2
    W1 --> LAKE
    W2 --> LAKE
  end

二、DuckDB 查询路径:Binder → Optimizer → Parallel Pipeline

DuckDB 官方 Internals(A 级)把路径概括为:Parser → Binder → Logical Optimizer → Physical Plan → Pipelines。与 Trino 的 Analyzed → Logical → Distributed plan 同构,但 没有 Distributed 层——所有算子在同一进程内调度。

2.1 与 columnar-engine/11 的分工

话题 columnar-engine/11 本篇
持久化 Row Group、Column Segment、checkpoint 不展开
外部文件 read_parquet 一句带过 scan 下推、投影裁剪、读湖扩展
对比对象 ClickHouse MergeTree Trino Page 流、DataFusion

2.2 向量化与 morsel-driven

第 7 篇 讲过三种执行语义;DuckDB 是 向量化 batch + morsel-driven 并行 的典型:表按 Row Group(默认约 122880 行)切分,每个 pipeline 线程处理一个 morsel,work-stealing 平衡负载。Trino Worker 上是 Page 流(列式 block),跨 Worker 才走 Exchange——这是分布式多出来的一层。

2.3 读湖:iceberg / delta 扩展

DuckDB 文档(Lakehouse formats,A 级)说明:

读路径复用 Parquet scan 的 data skipping(row group/file)、projection pushdown(与 lakehouse/18 ③④ 层衔接)。Planning 全在单进程内完成,没有 Trino 的 split 调度与 dynamic filter 跨节点等待(Trino 481 docs, Dynamic filtering)。


三、Apache DataFusion:库,不是产品

DataFusion(Apache 项目,A 级:Concepts / physical optimizer)是 Rust 写的可嵌入查询引擎

组件 职责
SQL parser / planner 可选 SQL 前端,或接收外部 LogicalPlan
Physical optimizer 规则链:谓词下推、投影裁剪、join 重排等
Execution Arrow RecordBatch 上的向量化算子
TableProvider 扩展读 Parquet、Iceberg(iceberg-rust)、Delta(delta-rs

DataFusion 不提供 Trino 式长期运行的 SQL server(社区有 Ballista 等分布式实验,生产主流仍是嵌入)。典型用途:在 Rust 服务内嵌 SQL、作为 Comet 等 Spark 向量化加速的执行层、原型验证 Substrait 计划。

与 DuckDB 对照:

维度 DuckDB DataFusion
交付形态 独立引擎 + 多语言绑定 Rust crate,嵌入宿主
存储 内置 DB 文件 + 外部 Parquet 无默认存储,靠 TableProvider
优化器 内置 C++ optimizer Physical rule 可插拔
读 Iceberg 官方 iceberg 扩展 社区 iceberg-rust,版本需核对 spec

选型 DuckDB:要开箱即用的 CLI、Python/R 分析、单机读湖。DataFusion:要把查询能力嵌进 自研 Rust 数据平面,或对接 Arrow 生态。


四、本机实验:计划形状与 Parquet 投影下推

4.1 小规模 join 的物理计划

reproduce/run_duckdb_experiments.py 在内存表上构造 TPCH 风格 orders(10 万行)与 customer(5000 行),DuckDB 1.5.4EXPLAIN 片段(经删减):

HASH_JOIN
  Conditions: o_custkey = c_custkey
  SEQ_SCAN memory.tpch.orders
    Projections: o_custkey, o_totalprice
    Filters: o_orderdate>='1996-06-01'::DATE
  SEQ_SCAN memory.tpch.customer
    Projections: c_custkey, c_name

可见 optimizer 在 scan 上做了 投影裁剪(只读 join/agg 需要的列)与 谓词下推(日期过滤在 orders scan)。无 shuffle:两表在同一进程 hash join。同一查询 3 次 wall time 中位数 0.0025 s(内存表、冷启动后,本机 WSL2)。

4.2 宽 Parquet:投影下推对 IO 的影响

写 20 列 × 50 万行 Parquet(row_group_size=100000,文件约 3.65 MiB),对比:

查询 EXPLAIN ANALYZE 扫描行数(filter 场景) 5 轮中位 wall(ms)
count(*) 全表 0.67
count(col_0) 单列 0.48
count(*) + col_0 窄区间 1000 rows(过滤后) 2.32

第三组在 scan 算子上出现 Total Files Read: 11000 rows,说明 row group 级 min/max 跳过 生效(与 lakehouse/18 实验二 同一机制)。单列 count(col_0)count(*) 快,体现 只读一列 chunk 的投影下推——宽表上这是数量级 IO 差异的来源。

4.3 实验边界


五、何时仍需要 Trino:嵌入式覆盖不了的场景

DuckDB 能读 Iceberg,不等于替代 Trino:

  1. 多租户并发:数十 BI 用户同时提交 SQL,需要 coordinator 队列、resource group(第 16 篇)。
  2. 跨源联邦:一条 SQL join Iceberg 表与 MySQL / Hive / Kafka 连接器——Trino Connector SPI 的设计目标。
  3. 分布式 dynamic filter:大事实表 join 小维表,build 侧 key 运行时下推到 probe 侧 scan(Trino 481 docs);单机引擎形态不同,收益模型也不同。
  4. 数据大于单机内存且无法局部裁剪:partition/file pruning 失效时,嵌入式只能垂直扩容;MPP 可水平加 Worker。

反过来,ETL 脚本内嵌聚合、笔记本 EDA、CI 里对湖表做 smoke query、无 JVM 环境读 Iceberg——DuckDB 运维成本更低。


六、小结


上一篇Spark SQL 与 Catalyst

下一篇Iceberg Connector 下推全链路

返回 系列目录


参考资料

  1. DuckDB Documentation, Internals overview / Vectorized Execution / Lakehouse Formats / Iceberg Extension(A 级)。
  2. Apache DataFusion Documentation, Concepts / Physical Optimizer(A 级)。
  3. Trino Documentation 476+, Overview Architecture、Dynamic filtering(引用,未本机实测)。
  4. 本机实验:DuckDB 1.5.4,Python 3.14 venv,WSL2;脚本 post/db/query-engine/reproduce/run_duckdb_experiments.py 与宽表 Parquet 辅助脚本(同环境一次性执行)。

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-07-07 · database / distributed

【分布式 OLAP 查询引擎】Trino · Spark · DuckDB · 优化与 MPP 执行

闭合数据平台栈最后一块:从 SQL 解析与 Calcite 式优化,到 Volcano/向量化执行、Trino Coordinator/Worker 与 shuffle,再到 Iceberg connector 下推与生产排查。承接 lakehouse 第 18 章读湖视角,补全「谁在做 planning」的引擎内核层。

2026-07-07 · database / distributed

【分布式 OLAP 查询引擎】Iceberg 下推全链路:Planner 视角

与 lakehouse/18 分工:那边讲四层读湖漏斗是什么;本篇讲 Trino/Spark/DuckDB 在 SQL 优化链的哪一步把谓词变成 layout constraint、谁调用 Iceberg planning、split 如何携带残余谓词。引用官方文档与 lakehouse/18 本机 PyIceberg 实测,不伪造 Trino 计划输出。


By .