本文翻译自:《Apache Iceberg: An Architectural Look Under the Covers》
原文链接: 点此跳转
Iceberg 架构
Iceberg三层架构:
Iceberg catalog
metadata
层,包含metadata file
、manifest list
和manifest file
data
层
Iceberg catalog
对Iceberg的任何读写操作,首先是要找到当前元数据指针的位置(注意:current metadata pointer
不是官方术语,而是描述性术语)。
Iceberg catalog 的主要要求是它必须支持用于更新当前元数据指针的原子操作(例如:HDFS,Hive MetaStore)。这就是为什么Iceberg表上的事务是原子性的,并能够提供正确性保障。在Iceberg catalog中,每个表都有一个指向该表当前元数据文件的引用。
Iceberg catalog的组织形式:
- 使用hdfs作为catalog,表的
metadata
文件夹下有一个名为version-hint.text
的文件,其内容是当前元数据文件的版本号。 - 使用Hive metastore作为catalog,hive metastore中的表条目有一个表属性,用于存储当前元数据文件的位置。
因此,当SELECT查询Iceberg表时,查询引擎首先话查询Iceberg catalog,然后检索他要读取的表的当前元数据文件的位置,然后打开该文件。
Metadata file
顾名思义 ,metadata file
存储当前表的元数据。包含相关表的schema,分区信息,快照列表以及当前快照信息。
metadata file 完整内容:
v2.metadata.json
1 | { |
当一个SELECT查询Iceber表时,先通过catalog中获取并打开当前元数据文件,然后查询引擎读取
current-snapshot-id
的值。然后,它使用该值在snapshots
中查找该快照的条目,然后检索该快照manifest-list
的值,并打开该位置指向的manifest-list
。
Manifest list
manifest list
保存了相关构成该快照的每个manifest file
的信息,如manifest file
的位置、它被添加为哪个快照的一部分,以及关于manifest file
所属的分区以及manifest file
跟踪的数据文件的分区列的下限和上限的信息。
eg:snap-1257424822184505371-1-eab8490b-8d16-4eb1-ba9e-0dede788ff08.avro
(转换成了json格式)
1 | { |
当SELECT查询Iceberg表并在从元数据文件中获取快照位置后为快照打开清单列表时,查询引擎随后读取清单路径条目的值,并打开清单文件。它还可以在这个阶段进行一些优化,例如使用行计数或使用分区信息过滤数据
Manifest file
manifest file
跟踪数据文件以及有关每个文件的其他详细信息和统计信息。
eg:eab8490b-8d16-4eb1-ba9e-0dede788ff08-m0.avro
(转换成json)
1 | { |
当SELECT查询Iceberg表并在从清单列表中获取其位置后打开清单文件时,查询引擎然后读取每个数据文件对象的
file-path
条目的值,并打开数据文件。它还可以在这个阶段进行一些优化,例如使用行计数或使用分区或列统计信息过滤数据。
Iceberg CRUD
create table
1 | CREATE TABLE table1 ( |
执行此操作后,Iceberg环境如下所示:
在上面,在数据库db1中创建了一个名为table1的表。该表有4列,并以order_ts timestamp列的小时粒度进行分区(稍后将详细介绍)。
当执行上面的查询时,在元数据层中创建了一个带有快照s0的元数据文件(快照s0不指向任何清单列表,因为表中还不存在数据)。然后更新db1.table1的当前元数据指针的目录条目,使其指向这个新的元数据文件的路径。
insert
现在向表中新增一条数据
1 | INSERT INTO table1 VALUES ( |
当执行此insert操作时,会发生如下过程:
- 首先会创建parquet格式的数据文件 —
table1/data/order_ts_hour=2021-01-26-08/00000-5-cae2d.parquet
- 然后,创建指向该数据文件的清单文件(manifest file)—
table1/metadata/d8f9-ad19-4e.avro
- 然后,创建指向此清单文件的清单列表(manifest-list)—
table1/metadata/snap-2938-1-4103.avro
- 然后,基于先前的当前元数据创建新的元数据文件,新的元数据文件具有新的快照
s1
以及保持对先前的快照s0
的跟踪,指向该清单列表 —table1/metadata/v2.metadata.json
- 然后,在catalog中自动更新
db1.table1
的当前元数据指针的值,使其指向这个新的元数据文件
在上述操作没完成之前,其他任何读取表的操作都不能看到当前表的状态和内容。
merge into/upsert
现在假设我们已经将一些数据放到我们创建的staging表中。在这个简单的实例中,每次订单发生变更时都会记录信息,并且我们希望保持此表显示每个订单的最新详细信息,因此如果订单id已经在表中,则更新订单金额,如果还没有该订单的记录,我们希望为这个新订单插入一条记录。在此示例中,staging表包括表中已经存在的订单的更新(order_id=123)和表中尚未存在的新订单,该更新发生在2021-01-27 10:21:46。
select
1 | SELECT * |
执行select语句,将发生一下过程:
- 首先查询引擎转到iceberg catalog
- 然后,开始检索db1.table1的当前元数据文件位置条目
- 然后,打开该元数据文件并检索当前快照
s2
的manifest list
位置 - 然后它打开这个
manifest list
,检索唯一manifest file
的位置 - 然后,打开
manifest file
,检索数据文件位置 - 最后读取这些数据文件,返回给client端