在业界的数据湖方案中有 Hudi、Iceberg 和 Delta 三个关键组件可供选择。
Iceberg 官网中是这样定义的:
Apache Iceberg is an open table format for huge analytic datasets
即 Iceberg 是大型分析型数据集上的一个开放式表格式。通过该表格式,将下层的存储介质(HDFS、S3、OSS等)、文件格式(Parquet、Avro、ORC等)与上层计算引擎(Flink、Spark、Presto、Hive等)进行解耦,如下图所示。
计算与存储的解耦给我们带来了更多的灵活性,在计算引擎上有了更多的选择,可以根据实际的需求选择不同的计算引擎。通过表格式屏蔽了下层的存储细节,对上层引擎呈现的都只是一张 Iceberg 表。
为了便于理解 Iceberg 的几个重要的特性,我们先简单介绍下 Iceberg 的文件的组织形式。
如下图所示,Iceberg 文件组织分为四层,分别为Metadata、Snapshot、Manifest、File。
Iceberg 的一个亮点是提供了 ACID 的语义支持,通过 Snapshot 进行读写分离,提供了 Serializable isolation,且所有的操作都可以保证原子性。
相比 Hive 而言,Iceberg 提供的事务性可以隔离写入任务队分析任务的不利影响,且写入失败不会出现脏数据。
更加吸引人的是 Iceberg 和 Flink 的结合,通过 Flink 的 Checkpoint 机制和 Iceberg 的事务性,可以做到端到端的 Exactly once 语义。
提起一张表(table format),我想最先强调的是表是具有 Schema的。 Iceberg 表是有 Schema 强制约束的。与 Hive 表不同,写入 Iceberg 表的数据必须经过 Schema 的校验,这样就解决了 Hive 表文件内容和 Schema 不匹配导致的下游消费异常的问题。
此外,Iceberg 在Metadata 文件中记录最新的 Scehma 结构,并通过 id 与实际的数据文件中的 Schema 进行映射。因此对 Iceberg 表可以进行更加灵活的 Schema 变更操作,你可以像 MySQL 那样对 Iceberg 表进行增加列、删除列、更新列等操作,并且这些操作不会有任何的副作用,而在使用 Hive 表进行 Schema 变更时,在某些情况下则需要将历史的分区数据全部重写才可以完成。
与 Hive 表类似,Iceberg 也可以进行分区来获取更快的查询。但与 Hive 不同的地方时 Iceberg 支持隐式分区。
在 Hive 中,分区需要显示指定为表中的一个字段,并且要求在写入和读取时需要明确的指定写入和读取的分区,如下示例中,以 event_date
作为分区分别展示读写 Hive 表和 Iceberg 表。
在上述例子中,Hive 表并不知道event_date
和event_time
的对应关系,需要用户来跟踪。
而在 Iceberg 中将分区进行隐藏,由 Iceberg 来跟踪分区与列的对应关系。在建表时用户可以指定date(event_time)
作为分区, Iceberg 会保证正确的数据总是写入正确的分区,而且在查询时不需要手动指定分区列,Iceberg 会自动根据查询条件来进行分区裁剪。
通过 Iceberg 隐式分区,我们将表分区从物理的目录结构和逻辑上分区进行分离,因此 Iceberg 可以更好的进行分区的变更。如下图所示,当我们将分区由按月分区调整为按天分区后,旧分区的目录结构保持不变,新数据将会使用新的目录结构写入。当进行查询时,会根据分区的不同分别执行不同的查询计划。
Iceberg 表的一大亮点是提供了 OLAP 场景下列式存储数据集的更新和删除的能力。由于 Iceberg 表支持更新和删除,因此你可以将 Changelog (如 Binlog 数据) 导入到 Iceberg 表中进行分析,同时也可以操作 Iceberg 表以更新或删除某一行或者一批数据。而在 Hive 中,由于 Hive 不支持更新,因此每次只能全量写入,浪费了计算资源,且存在较多的冗余数据。
Iceberg 表的更新是采用 MOR (Merge on Read) 模式来实现的,任何的更新和删除操作并不会对原数据文件进行操作,而是采用追加的形式写入到另一个文件中(Delete File),在读取时进行 Merge 操作以获取正确的结果。
由于 Iceberg 多版本的设计,允许用户在多个快照之间进行切换。通过 Time travel 用户可以实现如下操作:
此外 Snapshots 记录了本次 Commit 的变更,可以通过查询、监控 Snapshots 的信息以监控数据的并更情况,及时发现数据的质量问题。
SELECT * FROM prod.db.table.snapshots
最后我们做一个总结:Iceberg 作为一种表格式,将上层计算引擎和下层存储进行了解耦,使对数据的管理和操作更加灵活;通过 Schema 的强约束和事务性保证了写入数据的一致性问题;得益于良好的文件组织形式,使表分区在逻辑上和物理目录进行了分离,且可以达到文件级别的索引,不需要遍历目录下所有的文件,这大大提升了查询的性能和使用的便捷性。