The Life of a Read/Write Query for Apache Iceberg Tables
Apache Iceberg 作为开源分层表格式,通过解耦数据与元数据管理,为大数据处理提供事务性与可扩展性。本文以读写查询生命周期为核心,解析 Iceberg 表的处理逻辑。
从 “Apache Iceberg 101” 基础概念出发,阐述查询引擎对读写请求的解析流程:元数据查询获取表结构与文件布局,扫描执行实现数据过滤与结果返回,结合时间旅行特性访问历史数据。写入部分聚焦插入、删除、upsert / 合并操作的底层实现,揭示 Iceberg 借助事务日志与文件管理保障数据一致性的机制。全文为开发者呈现 Iceberg 高效管理数据读写生命周期的全流程视角,助力理解其在大数据场景中的应用价值。
原文:https://www.dremio.com/blog/the-life-of-a-read-query-for-apache-iceberg-tables/
Apache Iceberg 101
存储层的结构如下:
Data Layer
Data files – Parquet or ORC 的存储格式,真实数据放这里
Delete files – 被认为是删除的数据
Metadata Layer
Manifest files – 一个快照的子集,这些文件用于追踪这些子集中独立的数据文件,以及进一步的元数据裁剪
Manifest lists – 定义一个表的快照,列出构成这个快照的所有 manifest 文件,以及进一步裁剪的元数据
Metadata files – 定义表,跟踪 manifest lists,当前以及前一个快照,schemas,以及 分区 schemas
The Catalog
这个指定当前的元数据文件,可以提供类似数据库的事务保证,以及元数据存储功能
官网的描述信息
Snapshots 格式细节描述
Manifests 格式细节描述
Table Metadata
JSON serialization
How a Query Engine Processes the Query
基于order表的查询,表结构
查询语句
Metadata Qeruy
查询首先被提交到 engine,然后被解析,engine 需要知道这个表的元数据,才能做 plan
查询元数据,四个步骤执行过程如下:
第一步,获取 catalog,根据 catalog 获取 orders 表的当前元数据文件
第二步,通过 order 表的最新的元数据,可以
拿到表的 schema
表的分区信息,之后在 plan 中可以做裁剪
当前快照的 “manifest list” 可以知道 需要再进一步扫描哪些文件
第三步,通过 manifest list 文件,我们可以再进一步
partition-spec-id,可以拿到分区的 schema 信息,当前的 order 只有一个分区
每个分区还包含了一些统计信息,通过 lower 和 upper 边界,可以可以确定 是否要跳过这个分区
当后面拿到 manifest file 时候还可以再做进一步的 skip
第四步,根据每个 manifest file 文件,可以继续做裁剪,然后获取每个文件对应的 data
schema-id, partition-id 包含的就是数据文件信息
内容的类型,比如可以跳过 delete 类型的数据
列中包含了 value 内容、唯一值、lower、upper 信息,可以做裁剪
Performing the Scan and Returning the Results
经过上面几次 裁剪后,真正要扫描的数据文件就很少了 parquet 文件本身也包含 min/max 信息,还可以继续在文件级别继续 裁剪
Time Travel
查询前一个版本的信息
这里使用了 TIMESTAMP AS OF ,用作 time travel 用的完整的执行过程如下:
通过最新的 快照,可以:
通过比较快照 id,或者 快照的 timestamp,找到 AS OF 之前的快照
获取这个表的 schema
获取查询分区,用于裁剪,以及目标快照的 manifest list
跟普查查询类似,找到 manifest list 文件
通过 partition-spec-id,可以知道分区信息
分解分区的统计信息,lower 和 upper 做进一步裁剪
在这里可以 skip 大量不需要的分区
对于 manifest files
schema-id、partition-id 对应于数据文件
跳过 delete 类型的文件
根据 统计信息,如 lower、upper,做裁剪
接下来就是 scan 数据文件,因为前面已经 skip 了大量的文件,此时需要扫描的文件就少很多了 parquet 文件本身也可以做谓词下推,进一步减少读取的数量整个过程,其实跟普通的读取差不多
How a Query Engine Processes the Write
Insert
查询语句
执行过程
查询被提交到 engine,解析,之后查询引擎开始解析
获取 catalog,拿到 schema,尽管只是 append,但要确认两件事
确保满足表的 schema,以及哪些字段不能为 null
获取表的分区,确定如何组织写入的数据
写入数据
因为是 insert,所以不会对已有文件造成影响,只要写入新文件即可
写入时根据表的分区 schema 信息做写入
如果设置了排序顺序,并且 engine 也支持,则写入也会按照指定要求写入
写入元数据
写入每个 manifest file 文件,每个文件包含了数据文件的具体路径,每个列的统计信息等
将本地快照中新增的 manifest file,已经存在的 manifest file,一起写入到 新的 manifest list 中
在这个 manifest list 中,包含了所有 manifest file 的路径,以及每个文件的信息,如每个分区的 lower 和 upper 等
创建一个 metadata file 用来汇总表信息,包含文件路径,以及 manifest list 信息等
提交
engine 再一次获取 catalog,以确保在写入的同时,没有新的快照出现
这是为了防止并发写入冲突
当出现冲突时第一个写入成功,后面的写入者会重试 3 或者 4,直到成功或者 重试失败退出
读取的时候总是拿到最新的快照,所以并发写入还没有提交时不会有影响
Delete
语句
执行过程
发送请求到 engine,准备解析
获取 catalog 拿到最新的 metadata file
根据表的分区信息,确定如何写入数据
获取当前的 序列 ID,事务序列 ID, 假设在完成之前没有其他的写入冲突,利用了 OCC 机制
写文件
写策略取决于表的删除策略:“copy-on-write” (COW) or “merge-on-read” (MOR)
如果是 copy-on-write
通过元数据,识别出哪些文件包含删除的数据
完整的读取这些文件,确定删除的内容
写一个新文件替换这个快照中的文件,新文件中不包含被删除的内容
如果是 merge-on-write 避免了重写数据文件,但包含了墓碑的删除数据文件,读取时需要进一步处理,包含两种删除
Position deletes
Equality deletes
Position deletes
通过扫描元数据,确定哪些文件被删除
通过扫描数据文件,确定要删除的记录位置
在分区中写一个删除类型的文件,记录了哪些记录是按位置从 哪些文件中删除的
Equality deletes
不需要扫描任何文件,只需要写一个删除类型的文件,列出哪些行中的值要被删除
在读取时,根据删除类型文件和分区中的数据记录做重建,这个操作代价很高
原始的文件仍然被保留,当后续读取时,会根据 删除类型文件,旧文件做合并处理
写元数据文件
开始写入 manifest file,每个文件包含 数据文件的路径,以及文件的 列统计信息,lower 和 upper 等
将新的 和存在的 manifest files 文件一起当做一个快照,写入到 manifest list 中
在新的 mainfest list 中包含所有 manifest file 的路径,以及统计信息,如增加了多少数据,lower、upper 等
再写入 metadata file,记录表的汇总信息,包括 manifest list 的路径和相信信息
提交记录
再次获取 catalog,同时确保在写入时候,没有出现新的快照
同样也是为了预防并发冲突的,失败会继续重试
读取总是拿到最新的快照,正在写入的不会影响读取
Upsert/Merge
语句
执行过程
解析查询,准备建立查询计划
检查 catalog
校验表的 schema,确认非空字段,获取分析信息
获取当前的 序列 ID,事务序列 ID,使用 OCC 做并发控制
写数据文件
同时从源、目标读取数据文件放入内存中,然后做匹配,比如根据 id 做比较,
copy-on-write,只要匹配更新任何一行,整个文件都需要读取,重写
merge-on-read,生成新的更新/删除/插入 数据文件,读取时会忽略旧的文件,只有更新的数据在内存中,占用内存会减少
如果 WHEN MATCHED 匹配,则做更新,则做插入操作
写元数据
跟之前的一样,写入 manifest files,以及数据文件路径、统计信息
当前快照中已经存在的 manifest file,跟新的一起,写入到 manifest list 中,以及 manifest file 的路径和统计信息
写入 metadata file 汇总表信息,包含 manifest list 路径和信息等
提交
再次获取 catalog 信息,确保这段时间没有写冲突,如果有冲突则继续重试
利用了 OCC 做并发控制,快照隔离级别,串行化隔离级别,来确保 ACID 事务
Reference
The Life of a Write Query for Apache Iceberg Tables
Maintaining Iceberg Tables – Compaction, Expiring Snapshots, and More
How Z-Ordering in Apache Iceberg Helps Improve Performance
What Is Apache Iceberg? Features & Benefits
Lakehouse Trifecta — Delta Lake, Apache Iceberg & Apache Hudi
Iceberg Table Spec
Youtub: CHUG Talks: Introduction to Apache Iceberg
Row-Level Changes on the Lakehouse: Copy-On-Write vs. Merge-On-Read in Apache Iceberg
Optimistic concurrency control
How Netflix uses eBPF flow logs at scale for network insight







评论