#database/olap
# 概述
Apache Iceberg is an **open table format** for **huge** analytic datasets。
特点:
- schema evolution
- hidden partitioning + partition layout evolution
- ACID事务
- time travel
- 多云支持
# 湖格式

- **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
删除时需要先查找被删除的记录在哪个文件的哪个位置。

## Equality delete
不需要读取老数据。

# 并发控制
- 读写之间通过MVCC实现快照隔离
- 多个写之间使用 metafile 重命名检测冲突
# 总结
- 架构简洁,外部依赖很少,要求低(底层的文件系统的能力等)
- hidden partition 设计是一个亮点
- 功能丰富程度不如 Hudi:如异步 compaction 等
# 引用
- [Iceberg Table Spec](https://iceberg.apache.org/spec/)