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

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

文章导航

分类入口
databasedistributed
标签入口
#iceberg#trino#spark#duckdb#predicate-pushdown#query-planner#layout-constraint#manifest#split

目录

lakehouse 第 18 章 把读湖拆成四层漏斗:partition pruning → file pruning → row-group/page pruning → 字典过滤,并说明每层元数据从哪来、数据布局如何决定 \(p_{\text{part}}\)\(p_{\text{file}}\)\(p_{\text{rg}}\)。那条文章回答 「裁剪链路是什么、为什么有效」

本文是 query-engine 系列 第 15 篇,回答 「谁在 planner 的哪一步发起裁剪、约束如何传到 Worker 上的 scan 算子」

不重复粘贴 lakehouse/18 的漏斗全文与 Parquet 格式细节;需要时跳转 lakehouse/1808 Iceberg 元数据02 Parquet

环境说明:本机 WSL2、无 JVM Trino/Spark。Trino/Spark planner 行为按 476+ / 3.5+ 官方文档与源码结构 归纳;不伪造 EXPLAIN 输出。裁剪效果数字复用 lakehouse/18 的 PyIceberg plan_files() + DuckDB 1.5.4 本机实测(A 级)。


一、与 lakehouse/18 的分工

问题 lakehouse/18 本篇(query-engine/15)
四层漏斗定义 ✅ 完整展开 仅引用,不复述
元数据字段含义 ✅ manifest bounds、PageIndex 引用 08
数据布局与 \(p_{\text{file}}\) ✅ 实测 user_id vs country 引用实验结论
SQL 优化器 IR 层级 不涉及 ✅ AST → Logical → Physical → Split
Trino IcebergMetadata 调用链 不涉及
Dynamic filter 与 planning 时序 概述 ✅ 与 第 5 篇 衔接
Split 携带什么 split 大小一句 ConnectorSplit + 残余谓词

读 lakehouse/18 建立 「读湖时发生了什么」;读本篇建立 「优化器如何发起读湖」。两篇合读才能从 SQL 文本追到 Worker 上的 Parquet reader。


二、从 SQL 谓词到 Layout Constraint

第 2 篇 之后,一条带 Iceberg 表扫描的查询在引擎内大致经历:

flowchart TB
  SQL["SQL + WHERE"] --> AN["Analyzer<br/>绑定表/列"]
  AN --> LOG["Logical Plan<br/>TableScan + Filter"]
  LOG --> OPT["Optimizer<br/>谓词下推/常量折叠"]
  OPT --> PHY["Physical Plan<br/>TableScanNode"]
  PHY --> META["Connector Metadata<br/>applyFilter / layout"]
  META --> ICE["Iceberg TableScan.planFiles()"]
  ICE --> SPL["Splits + residual predicate"]
  SPL --> SCAN["Parquet PageSource"]

关键分界:

  1. 引擎内优化(Trino planner、Spark Catalyst):常量折叠、谓词合并、join 下推——产出对 TableHandleTupleDomain 或等价约束(Trino SPI ConnectorMetadata.applyFilter)。
  2. Connector + Iceberg 库:把 TupleDomain 映射为 Iceberg Expression,调用 TableScan.planFiles() 做 manifest 层 partition/file pruning(Iceberg spec, Planning;A 级)。
  3. Worker scan:对每个 split 打开 Parquet,做 row-group/page pruning;残余谓词在向量算子上求值。

lakehouse/18 的 funnel 主要覆盖 步骤 2 的后半 + 步骤 3;本篇聚焦 步骤 1→2 的接口步骤 2→split 的分发


三、Trino:Metadata 层注入约束

Trino Iceberg connector(文档 476+,源码 trinodb/trino plugin/trino-iceberg,A 级)在 planning 阶段的典型职责:

阶段 组件 输入/输出
解析表 IcebergMetadata.getTableHandle 表名 → IcebergTableHandle(snapshot、schema)
下推过滤 applyFilter / applyProjection Logical 约束 → 更窄的 handle + 未下推残余
布局 getTableLayouts / 新 API 路径 调用 Iceberg planning,得 文件清单
分片 IcebergSplitManager.getSplits 文件 → IcebergSplit(path、offset、length、delete files)
扫描 IcebergPageSourceProvider Split → Parquet reader + 动态过滤

3.1 常量折叠与分区 transform

Trino 对 Iceberg 隐藏分区 的谓词改写(constant folding + desugaring)发生在 引擎 optimizer,再经 applyFilter 进入 Iceberg(Trino blog, Just the right time date predicates with Iceberg, 2023-04-11,B 级)。这与 lakehouse/18 第二节「partition pruning 在 manifest 层」衔接:引擎先改写 SQL 谓词,Iceberg 库再在 manifest entry 上过滤

3.2 Dynamic filter 与 split 生成时序

第 5 篇 讲过 broadcast/partitioned join;Trino dynamic filter 在 build 侧收集 join key,运行时合并进 probe 侧 scan 的 constraint(Trino 481 docs, Dynamic filtering)。Planning 因此可能是 两阶段

  1. Coordinator 先基于静态谓词 planFiles,生成初始 split。
  2. Join build 完成后,probe scan 等待 dynamic filter(iceberg.dynamic-filtering.wait-timeout),再缩小实际读范围。

这是 query-engine 独有 的时序问题:lakehouse/18 讲静态漏斗,本篇补 MPP 上 runtime 约束如何并入 scan

3.3 EXPLAIN 读什么(不贴未实测输出)

Trino EXPLAIN ANALYZE 中关注(文档 SQL explain):

本机未跑 Trino 时,用 lakehouse/18 的 PyIceberg 数文件 + 文档字段对照,避免伪造 CLI 输出。


四、Spark Catalyst:DataSource V2 扫描

Spark 3.5+ 读 Iceberg 走 DataSource V2(Spark SQL Guide;Iceberg Spark 文档,A 级):

阶段 Spark 侧 Iceberg 侧
分析 V2ScanRelation 解析 catalog 表
优化 PushDownUtils 谓词/投影下推 SparkScanBuilder 接收 filter
物理 BatchScanExec planFiles() 文件任务
执行 Executor 读 Parquet 同 Trino ③④ 层

df.explain() / SQL UI 看 PushedFiltersPartitionFilters(lakehouse/18 附录 B)。AQE第 13 篇)在 shuffle 后改分区数,不改变 manifest planning 接口,但会改 scan 并行度


五、DuckDB:单进程内 Metadata + Scan

DuckDB iceberg 扩展(文档 Lakehouse formats,A 级)在 同一进程 完成:

  1. SQL optimizer 下推 filter/project 到 IcebergScan 物理算子。
  2. 扩展读 snapshot/manifest,等价 Iceberg planFiles()
  3. 复用 Parquet scan 做 row-group 跳过。

无 Coordinator:没有 split 跨网络分发EXPLAIN ANALYZETotal Files Read 与 scan 行数(lakehouse/18 实验二、本篇 第 14 篇 宽表实验)。


六、四层漏斗:Planner 发起点对照

漏斗层 主要发起者 Trino 可见性 Spark DuckDB
① partition Iceberg planFiles(),谓词由 engine 折叠后传入 Layout / constraint PartitionFilters scan 算子 Filters
② file min/max 同上 input size 下降 PushedFilters + 文件数 Total Files Read
③ row-group/page Parquet reader(Worker/进程内) Scan CPU vs input rows Executor scan EXPLAIN ANALYZE 行数
④ dictionary Parquet reader 少单独字段 同左 同左
Runtime Dynamic filter → 重新约束 scan dynamicFilters Runtime filtering(版本相关) join filter 下推(形态不同)

能力边界以各引擎 当前文档 为准,不做跨引擎 latency 排名。


七、实验:复用 lakehouse/18 的 Metadata Planning 数字

下列结果来自 lakehouse/18 本机 PyIceberg 0.11.1 真实执行(10 个按天分区文件),证明 步骤 2 planFiles() 的裁剪——与 Trino applyFilter 后调用 Iceberg planning 的语义一致(Iceberg Java/Python API 同 spec):

谓词 裁剪类型 扫描数据文件数
10
event_ts 单日 partition pruning 1
user_id 窄区间 file pruning(min/max) 1
country = 'CN' 无法 file prune 10

Planner 视角解读

Row-group 层:lakehouse/18 实验二 DuckDB 1.5.4 对排序 Parquet 窄区间扫描 10000 行 vs 打乱文件,中位耗时 2.6 ms vs 6.3 ms——发生在 Split 打开文件之后,③ 层与引擎无关布局的数据仍一致。


八、常见「下推失效」在 Planner 上的信号

现象 Planner 层根因 查什么
扫全表文件 谓词未进 applyFilter(函数包裹列、类型不匹配) EXPLAIN 是否 Filter 在 Scan 之上
分区未裁 非确定性函数、缺 transform 推导 SQL 是否裸列比较常量
文件未裁 manifest 无 bounds / 列未索引 $files / PyIceberg plan_files
行组未跳 数据未排序、无 PageIndex Parquet footer
Join 仍扫大表 无 static 谓词且无 dynamic filter Trino dynamicFilters;join 类型

排查顺序:先确认约束是否进入 Connector,再查 Iceberg stats,最后查 Parquet 布局——与 lakehouse/18 附录 A 自检清单正交:那边偏 数据与格式,本篇偏 计划形状


九、小结


上一篇DuckDB 与 DataFusion

下一篇内存、Spill 与资源隔离

返回 系列目录 · lakehouse/18 读湖漏斗


参考资料

  1. Apache Iceberg, Table Spec — Planning、planFiles()(A 级)。
  2. Trino Documentation 476+, Iceberg connectorConnectorMetadataDynamic filtering(A 级;本机未实测 CLI)。
  3. Apache Spark SQL Guide 3.5+, Iceberg Spark 文档 — V2 pushdown(A 级)。
  4. DuckDB Documentation, Iceberg Extension(A 级)。
  5. lakehouse/18 本机 PyIceberg + DuckDB 1.5.4 实验(A 级)。
  6. Trino Blog, Just the right time date predicates with Iceberg, 2023-04-11(B 级)。

同主题继续阅读

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

2026-07-07 · database / distributed

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

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


By .