Iceberg作为凌驾于HDFS和S3等存储系统之上的数据组织框架,提供了数据写入、读取、文件管理和元数据管理等基本功能,虽然Iceberg提供了丰富的API接口,但是面向API开发需要使用方比较了解其原理和实现细节,还是显得门槛过高。此外,在面向实时数据读写场景,需要有一个桥接框架来自动完成数据的读写,于是Iceberg和Flink成为天作之合,本文就来研究下Iceberg是如何跟Flink对接的。
Flink写入Iceberg总体流程介绍
Flink典型的数据处理链路是Source->Transform->Sink,对Iceberg来讲,也遵从这一模式,比如下图:
Custom Souce是自定义的数据源类型的Source,用于向下游发送数据,比如下面的数据来源于静态List集合:
DataStream<RowData> dataStream = env.fromCollection(list)
IcebergStreamWriter起着数据变换作用,跟Source 组成链式Operator,IcebergFilesCommiter作为Sink,将数据提交到本地文件test表。
Source端发送数据到IcebergStreamWriter,IcebergFilesCommiter将从IcebergStreamWriter获取的数据提交到元数据管理系统,比如Hive Metastore或者文件系统。当成功提交元数据之后,写入的数据才对外部可见。在这一个过程中,IcebergStreamWriter除了相当于上述链路模式中的Transform角色之外,还有一个重要原因:实现事务提交隔离。IcebergStreamWriter将数据暂时写入到一个缓冲文件,该文件暂时对外部是不可见的,然后IcebergFilesCommiter再将IcebergStreamWriter写入的文件的元信息,比如路径、文件大小,记录行数等写入到ManifestFile中,最后将ManifestFile文件元信息再写入到ManifestList(ManifestList即快照信息),ManifestList又被写入以版本号区分的metadata文件中(v%版本号%.metadata.json),下图展示了一个完整的数据包括元数据组织示例:
下面展开来讲下实现上述目标的细节内容:
首先Source端DataStream数据流经过IcebergStreamWriter变换,生成新的DataStream: SingleOutputStreamOperator ,输出类型是WriteResult:
//DataStream<RowData> input
//IcebergStreamWriter streamWriter
SingleOutputStreamOperator<WriteResult> writerStream = input
.transform(operatorName(ICEBERG_STREAM_WRITER_NAME), TypeInformation.of(WriteResult.class), streamWriter)
.setParallelism(parallelism);
其中WriteResult的定义如下:
public class WriteResult implements Serializable {
private DataFile[] dataFiles;
private DeleteFile[] deleteFiles;
private CharSequence[] referencedDataFiles;
...
}
从类定义可知,IcebergStreamWriter的输出结果其实只是该过程产生的数据文件,主要包括DataFile和DeleteFile,referencedDataFiles暂时先不关注。
然后,IcebergFilesCommiter对上游的Operator 做变换,生成新的DataStream:SingleOutputStreamOperator,这个过程只是提交元数据,本身不会再往下游发送数据,所以返回数据类型为Void:
//SingleOutputStreamOperator<WriteResult> writerStream
SingleOutputStreamOperator<Void> committerStream = writerStream
.transform(operatorName(ICEBERG_FILES_COMMITTER_NAME), Types.VOID, filesCommitter)
.setParallelism(1)
.setMaxParallelism(1);
IcebergStreamWriter和IcebergFilesCommiter实现详细分析
从上面介绍可知,IcebergStreamWriter和IcebergFilesCommiter是最主要的两个数据处理过程,下面对其详细介绍。IcebergStreamWriter和IcebergFilesCommiter都是AbstractStreamOperator的子类,本身除了要实现对单个元素的处理逻辑,还有对快照处理的相关逻辑,先说说对单个元素的处理逻辑:
class IcebergStreamWriter<T>