??Iceberg 作为一个开放的 table format,从设计之初就有一个高度抽象的良好框架。本节将带领大家领略 Iceberg API 模块下与表相关的内容,主要涉及表的设计、分区规范、快照管理等,但本节将只讲接口设计相关,具体 Iceberg 对相关部分的核心具体实现,后述再详述。 ??进入正题,本次既然是讲与表相关的设计,那么我们肯定要从一等公民 Table 开始。
Table
在Iceberg中,Table 是顶层的核心概念之一,为了更好的理解它,我们首先将这个接口的方法分类列出,读者可以不必过于关心每一个方法的细节,大概有个印象即可。
接口梳理
我们来看一下这个接口主要包含了哪些方法:
- 表结构相关:
default String name()
Schema schema();
Map<Integer, Schema> schemas();
PartitionSpec spec();
Map<Integer, PartitionSpec> specs();
SortOrder sortOrder();
Map<Integer, SortOrder> sortOrders();
Map<String, String> properties();
String location();
- 分别有对应的更新操作:
UpdateSchema updateSchema();
UpdatePartitionSpec updateSpec();
UpdateProperties updateProperties();
ReplaceSortOrder replaceSortOrder();
UpdateLocation updateLocation();
- 快照相关:
Snapshot currentSnapshot(); // 得到当前快照
Snapshot snapshot(long snapshotId);
Iterable<Snapshot> snapshots();
List<HistoryEntry> history(); // Get the snapshot history of this table.
ExpireSnapshots expireSnapshots();
ManageSnapshots manageSnapshots();
- 表查询与操作相关内容:
// 查询
void refresh();
TableScan newScan();
// 插入
AppendFiles newAppend();
default AppendFiles newFastAppend() {
return newAppend();
}
RowDelta newRowDelta();
// 更新
RewriteFiles newRewrite();
OverwriteFiles newOverwrite();
RewriteManifests rewriteManifests();
ReplacePartitions newReplacePartitions();
// 删除
DeleteFiles newDelete();
// 回退
@Deprecated Replaced by {@link #manageSnapshots()}
Rollback rollback();
// 事务
Transaction newTransaction();
// 底层IO
FileIO io();
LocationProvider locationProvider();
// 数据文件加解密
EncryptionManager encryption();
表概述
Iceberg 既然是一种 table format,那么 Table 自然是系统内巡视天下的王者,它的设计展现了 Iceberg 的若干核心思想。
-
快照 从上述表的相关方法中,我们可以看到一些 Iceberg 的特色功能,其中最为明显的也许便是快照相关的内容。 在 Iceberg 中,快照是表在某个时刻的状态,每个快照都列出了在创建快照时构成表内容的所有数据文件,数据文件存储在多个清单文件中,而快照的清单文件则在清单列表文件中被详细记录。 与快照相关的设计,可以让Iceberg的表从容完成时间旅行、快照隔离等特性。 -
IO 在上述所列出的一系列方法中有两个比较特殊: location 与 FileIO ,它们看似不起眼,却至关重要。Iceberg 既可以说是云原生的表,有一部分便是因为对 IO 的良好抽象。FileIO 是核心 Iceberg 库和底层存储之间的主要接口,与Hive等组件不同的是,Iceberg 不引用目录,它追踪了表在文件级别的完整状态,通过 FileIO 可以从最顶层的元数据直达底层存储,这部分留待后续 IO 部分进行解读。 -
元数据变更 除了快照之外,我们还可以看到与一些与表的 Schema 变更相关的方法、类,包括 partition、orderby 等。Iceberg 对表的 Schema 变更具有良好的支撑,在不同的快照中,表的 Schema 可能是不同的,在此背景下,specs()\sortOrders() 等可以列出相关内容方法就具有特别重要的意义。 其中让我们先来看一下 PartitionSpec 。PartitionSpec 也即是分区规范的意思,它是对表内数据如何分布的描述,一个 Iceberg 表的分区规范会包含两部分,一个是分区的源列,一个是转换函数。转换函数将从源列派生出具体的分区列值。例如 date(ts) 会生成与名为 ts 的时间戳列关联的日期,它是 Iceberg 实现分区隐藏的关键所在之一,不过本节我们不进行赘述,这一部分留待后面再进行解读。 -
数据操作 接下来,我们看一下与表的数据操作相关的内容。 首先是 newRowDelta ,它是 Iceberg 提供行操作的顶层抽象,在 Iceberg 第二版元数据规范中,行级删除是其最大的特性之一。
RowDelta newRowDelta();
我们来看一下它的官方注释:
Create a new row-level delta API to remove or replace rows in existing data files.
可以看到,它所返回的 RowDelta 是进行行级变更的API,主要提供了增加、删除以及快照验证等相关的操作。
其次是newAppend newFastAppend。
AppendFiles newAppend();
default AppendFiles newFastAppend() {
return newAppend();
}
可以看到,newFastAppend的默认实现是newAppend,那它们两者有什么区别呢? 我们还是先从注解上看: newAppend :
Create a new append API to add files to this table and commit.
newFastAppend :
Create a new append API to add files to this table and commit. Using this method signals to the underlying implementation that the append should not perform extra work in order to commit quickly. Fast appends are not recommended for normal writes because the fast commit may cause split planning to slow down over time. Implementations may not support fast appends, in which case this will return the same appender as newAppend().
在 newFastAppend 的注解中提到,此方法需要避免一些额外工作,以加速commit的过程,但它会带来的影响是,如果过多使用,可能造成 split 规划过程缓慢。 那么什么是额外的工作呢,其实,我们在 Iceberg 对 AppendFiles 的两个实现类 FastAppend \ MergeAppend 中可以分析出,这些过程主要是 manifest 文件合并等操作,它会减少元数据的文件数量,提升扫描查询规划的速度。
本节就先写到这里,关于覆写、删除等操作,以及对相关接口的理解分析,我们后文再叙。
|