#database/olap # 概述 Apache Iceberg is an **open table format** for **huge** analytic datasets。 特点: - schema evolution - hidden partitioning + partition layout evolution - ACID事务 - time travel - 多云支持 # 湖格式 ![|500](https://iceberg.apache.org/img/iceberg-metadata.png) - **catalog**:有哪些表,表当前的metadata file位置 - **metadata file**:json格式,表示table当前的 state,包括 - table schema - table partition config - data snapshot(图中的 S0,S1等),metadata中会保存多个历史snapshots,一个snapshot表示一个版本。 每次修改都会创建一个新的 metadata file,并将 catalog 中 table 的 metadata pointer 原子地替换为新 metadata file 的位置。 - **manifest list**:对应一个snapshot(每次修改提交都会创建一个新的 manifest list),它存储了 snapshot 中有哪些 manifest。 - **manifest**:存储了数据文件的列表,和每个数据文件的统计信息。 详细讲解:[Apache Iceberg Architecture Overview - 101 Course #4](https://www.youtube.com/watch?v=tZ3C_CAALfE) # Hidden partition 没有物理上的分区目录。 ## 分区定义 只需要在CREATE TABLE定义分区。分区的定义由多个 partition field 组成。 每个 partition field 关联一个 transform(类似一个函数),定义如何根据数据中的某列来计算 partition value。 **例1**:一个 partition field,计算方式为直接使用原表中的category列值作为 partition value ```sql CREATE TABLE prod.db.sample ( id bigint, data string, category string) USING iceberg PARTITIONED BY (category) // ``` **例2**: 三个 partition field 1. 按 id 列 hash 分桶(16个) 2. ts 列上应用 day 函数(tranform) 3. category列 ```sql CREATE TABLE prod.db.sample ( id bigint, data string, category string, ts timestamp) USING iceberg PARTITIONED BY (bucket(16, id), days(ts), category) ``` ## 分区实现 - **写入:** 同一个 data file中的所有数据都属于相同的分区,即 data file 中每行计算出来的 partition values 都是相同的。 - **查询:** iceberg 在 manifest 中存储了每个 data file 属于哪个分区(即 data file 的 partition values)。 当查询中带有分区列相关的过滤条件时,可以高效地过滤掉不符合过滤条件的数据文件。 不仅对于分区相等条件,大于等于,或者只指定部分分区列条件等,都有过滤的能力。 过滤时先对 partition 相关列应用 tranform,按照转换后的值进行过滤。 两层过滤: 1. 读取 manifest list文件,检查每个 manifest 的 partition 统计信息,跳过不符合条件的 manifest。统计信息主要是该 manifest 中(也就是它所有包含的所有data flies)各个 partition field 的 value 范围、null 信息等。 2. 读取候选 manifest 文件,检查每个 data file 的 partition values,过滤 data file。例如一个 event 表有一个 timestmap列 ts ,分区定义为 `ts_day=day(ts)` ,查询条件`ts > X`会被转换为 `ts_day >= day(X)` ## 分区修改(Evolution) Iceberg 支持通过 DDL 命令修改分区的方式,修改后只作用于新数据。 修改后分区过滤能力仍然有效(用户不需要更改查询条件)。 查询时老数据基于老的 partition 配置过滤(一个 manifest 只能包含一个 partition spec)。 比如将分区定义由 ts_day=day(ts) 改为 ts_hour=hour(ts) 原来的查询条件 ts > X 不需要修改。 # MOR V1 版本只支持 Copy-On-Write,[V2 版本](https://docs.google.com/document/d/1FMKh_SQ6xSUUmoCA8LerTkzIxDUN5JbStQp5Hzot4eo)开始支持 Merge-On-Read。 MOR 主要通过 delete file 来实现。 delete file 有两种类型: - position delete - equality delete [flink cdc writer](https://github.com/apache/iceberg/blob/9e76a846d2e08dcc471071031333efc16eae90e3/flink/v1.16/flink/src/main/java/org/apache/iceberg/flink/sink/BaseDeltaTaskWriter.java) ## Position delete 删除时需要先查找被删除的记录在哪个文件的哪个位置。 ![](https://apijoyspace.jd.com/v1/files/EVX8WrAH8nxm0kgD8WWL/link) ## Equality delete 不需要读取老数据。 ![](https://apijoyspace.jd.com/v1/files/Il7aGR3rPzztxgVXkaGq/link) # 并发控制 - 读写之间通过MVCC实现快照隔离 - 多个写之间使用 metafile 重命名检测冲突 # 总结 - 架构简洁,外部依赖很少,要求低(底层的文件系统的能力等) - hidden partition 设计是一个亮点 - 功能丰富程度不如 Hudi:如异步 compaction 等 # 引用 - [Iceberg Table Spec](https://iceberg.apache.org/spec/)