写点什么

HashData 的湖仓一体思考:Iceberg、Hudi 特性讲解与支持方案

  • 2024-03-05
    北京
  • 本文字数:3937 字

    阅读完需:约 13 分钟

湖仓一体作为一种新兴的开放式数据管理架构,能够充分发挥数据湖的灵活性、生态丰富以及数据仓库的企业级数据分析能力,已经成为企业建设现代数据平台的热门选择。


在此前的直播中,我们分享了 HashData 湖仓一体方案架构设计与 Hive 数据同步。本次直播,我们介绍了 Iceberg、Hudi 的特性与支持方案,并对 HashData 连接组件的原理和实现流程进行了详细的讲解和演示。以下内容根据直播文字整理。

Hudi 与 Iceberg 技术应用场景

在企业数据平台建设过程中,随着数据量的持续增加与场景的丰富,每家企业都会基于自有技术路线和需求,发展出形态各异的架构设计。


数据湖作为一种不断演进、可扩展的大数据存储、处理和分析基础设施,允许企业存储任意规模的结构化和非结构化数据。伴随着云存储(尤其是对象存储)技术逐步成熟,数据湖的解决方案也逐步向云原生靠近,数据处理方式由批处理向流式处理发展。


在这样的背景下,现代数据湖需要具备强大的流批处理能力、高效的数据更新机制、严谨的事务支持以及灵活多变的存储和计算引擎。


面对上述需求,传统的 Hive+HDFS 架构数据仓库存在数据修改成本高、不支持事务(ACID)、无法实现流批统一、数据分析用时长等“痛点”,无法直接用于建设数据湖。近些年,Hudi 和 Iceberg 等先进的表格式管理技术,凭借开放的文件存储格式、丰富的事务支持以及高效的读取写入等特点,成为企业数据湖建设的主流选型。

Hudi 基本术语与写入操作流程

Hudi 的诞生是为了解决 Hadoop 体系内数据更新和增量查询的问题,在数据存储、查询等方面均具有鲜明的特性。

FileLayouts

Hudi 的文件布局是其实现增量查询、数据更新等特性的基础,每个 Hudi 表有一个固定的目录,存放元数据(.hoodie)以及数据文件,其中数据文件以分区方式进行划分,每个分区有多个数据文件(基础文件和日志文件),这些数据文件在逻辑上被组织为文件和文件组。

  • Base File:列式存储的数据文件,默认是 Parquet 格式。

  • Log File:行存储的数据文件,为 avro 格式,保存的是数据的变更日志(redo log),会定期与 Base File 进行合并。

  • File Group:同一分区下,具有相同 fileId 的所有 BaseFiles + LogFiles 集合,一个分区可以有多个文件组。

  • File Slice:同一分区下,具有相同 fileId 以及相同 instant 的 BaseFiles + LogFiles 集合。

Timeline

可以理解为 Hudi 表的一个时间线,记录了 Hudi 表在不同时刻的操作,并保证操作的原子性。Timeline 包含 action、time、state 三个字段。

Table Types

Hudi 提供了两种表类型,分别为 Copy-On-Write(COW 表)和 Merge-On-Read(MOR 表):

  • COW 表:仅使用列式文件格式(如 parquet)存储数据。通过在写入期间执行同步合并,简单地更新版本和重写文件,适合更新数据量较大、时效性要求不高的场景。

  • MOR 表:使用基于列+基于行(如 avro)的文件格式的组合存储数据,更新被记录到增量文件中(基于行),然后被压缩以同步或异步地生成新版本的列式文件,适用更新数据量小、时效性要求高的场景。

Query types

Hudi 支持三种查询类型,分别为 Snapshot Query、Read Optimized Query、Incremental Query:

  • Snapshot Query:查询最近一次 Snapshot 的数据,也就是最新的数据。

  • Read Optimized Query:针对 MOR 表特有的一种查询方式,只读取 BaseFile,不合并 Log,因为使用的都是列式文件格式,所以效率较高。

  • Incremental Query:用户需要指定一个 commit time,然后 Hudi 会扫描文件中的记录,过滤出 commit_time 大于 begintime 的 TimeLine 记录及 BaseFile,可以有效地提高增量数据处理能力。

Writing

在 Hudi 数据湖框架中支持三种方式写入数据:Upsert、Insert 以及 Bulk-Insert。其中,Upsert 为默认行为,也是 Hudi 的核心功能。


图 1:Spark 写入 Hudi 操作流程示意图

如图 1 所示,Spark 写入 Hudi,Upsert 执行核心操作如下:

  1. 开始提交:判断上次任务是否失败,如果失败会触发回滚操作。然后会根据当前时间生成一个事务开始的请求标识元数据。

  2. 构造 HoodieRecord Rdd 对象:Hudi 会根据元数据信息构造 HoodieRecord Rdd 对象,方便后续数据去重和数据合并。

  3. 数据去重:一批增量数据中可能会有重复的数据,Hudi 会根据主键对数据进行去重,避免重复数据写入 Hudi 表。

  4. 数据 fileId 位置信息获取:在修改记录中可以根据索引获取当前记录所属文件的 fileld,因数据合并时 Update 操作需要知道向哪个 fileid 文件写入新的快照文件。

  5. 数据合并:在 COW 表模式中会重写索引命中的 fileId 快照文件;在 MOR 表模式中根据 fileId 追加到分区中的 log 文件。

  6. 完成提交:在元数据中生成 xxxx.commit 文件,只有生成 commit 元数据文件,查询引擎才能根据元数据查询到刚刚 Upsert 后的数据。 

  7. 数据清理:用于删除旧的文件片,以及限制表空间的增长,清理操作在每次写操作之后自动被执行,同时利用缓存在 TimeLine Server 上的 TimeLine Metadata 来防止扫描整个表。

  8. Compaction 压缩:主要是 MOR 模式中才会用到,会将 MOR 模式中的 xxx.log 数据合并到 xxx.parquet 快照文件中去。

lceberg 基本术语与写入操作流程

Iceberg 的官网定位是“面向海量数据分析场景的高效存储格式”,所以它没有像 Hudi 一样模拟业务数据库的设计模式(主键+索引)来实现数据更新,而是设计了更强大的文件组织形式来实现数据的 Update 操作。

Data files(数据文件)

数据文件是 Apache Iceberg 表真实存储数据的文件,一般是在表的数据存储目录的 data 目录下,如果我们的文件格式选择的是 parquet,那么文件是以“.parquet”结尾,Iceberg 每次更新会产生多个数据文件。

Snapshot(表快照)

快照代表一张表在某个时刻的状态,每个快照里面会列出表在某个时刻的所有 Data files 列表。Data files 存储在不同的 Manifest files 里面,Manifest files 存储在一个 Manifest list 文件里面,而一个 Manifest list 文件代表一个快照。

Manifest file(清单文件)

Manifest file 是一个元数据文件,它列出组成快照(Snapshot)的数据文件(Data files)的列表信息。每行都是每个数据文件的详细描述,包括数据文件的状态、文件路径、分区信息、列级别的统计信息(比如每列的最大最小值、空值数等)、文件的大小以及文件里面数据行数等信息。其中,列级别的统计信息可以在扫描表数据时过滤掉不必要的文件。Manifest file 是以 avro 格式进行存储的,以“.avro”后缀结尾。

Manifest list(清单列表)

Manifest list 也是一个元数据文件,它列出构建表快照(Snapshot)的清单。这个元数据文件中存储的是 Manifest file 列表,每个 Manifest file 占据一行。每行中存储了 Manifest file 的路径、其存储的数据文件(Data files)的分区范围,增加了几个数文件、删除了几个数据文件等信息,这些信息可以用来在查询时提供过滤,加快速度。


图 2:Iceberg 写入流程示意图

在向 Iceberg 写入数据时,其内部的工作流程可以概括为以下几个步骤:

  1. 生成 FileAppender:根据所配置的文件格式,Iceberg 会生成对应 FileAppender,这是实际执行写文件操作的组件。

  2. 写入数据文件:FileAppender 负责将数据写入到目标文件中。

  3. 收集统计信息:所有数据写完后,Iceberg 会收集写入的统计信息,如记录数(record_count)、下界(lower_bound)、上界(upper_bound)、值计数(value_count)等,以上信息对后续生成 Manifest file 提供重要输入文件。

  4. 生成 Manifest file:基于统计信息,Iceberg 生成对应的 Manifest 文件,Manifest 文件是 Datafile 的索引,保存了每个数据文件的路径等信息,Iceberg 根据这些 Manifest file 实现对文件的组织和管理。

  5. 信息回传:Executor 端将生成的 Manifest 文件和其他相关信息传回给 Driver 端,完成整个写入过程。

Hashdata 连接器工作原理及实现流程

数据湖中的数据通常未经组织或处理,直接分析的效率受限。HashData 通过自研 Hudi、Iceberg 连接器,实现了与这两种架构的流畅集成。HashData 目前对于 Hudi、Iceberg 支持 Readonly 表,不支持 Write。

图 3:HashData 连接器工作原理示意图

如上图所示,HashData 连接组件通过创建外部表的方式读取 Hudi、Iceberg 数据,进一步对湖内数据进行分析使用。

创建外部表
  1. 首先,需要 Hudi、Iceberg 存在需要读取的表。我们通过 Spark、Flink 等组件在 Hudi、Iceberg 上创建表并写入数据,且指定为 Hudi、Iceberg 格式。

  2. 在 HashData DB 上提交创建一张对应的可读外部表,外部表信息包含:Path、Catalog Type 等信息,也就是我们前文提到的位置相关信息。

  3. 接下来调用 Hudi、Iceberg 客户端,客户端会新建连接调用 Get Table,并传入外部表信息来获取 Hudi、Iceberg 表的元数据信息,包括表的字段数量、字段名、数据类型等。

  4. 根据获取到的元数据信息,在 DB 上 mapping 生成 HashData 的表信息。

  5. 至此,创建一张对应 Hudi、Iceberg 的外部表流程结束。

上述步骤,都是通过连接组件完成,相当于把表的 Path、Catalogtype 等信息打包传给连接器。连接器在获取相关表信息后再传递回来,HashData 把传回的信息 mapping 为可读外部表。

Select 表流程
  1. 当发起 Select 查询语句后,HashData 会在内部发起 Query For Select,通过连接器把查询的相关参数打包;然后通过 External Scan 的 Filter(比如 SQL 里的 where 条件)传给连接器。

  2. 连接器再调用 Hudi、Iceberg 的 Scan 接口,Scan 方法会得到传入的参数,根据这些参数去过滤查询这次表相关的所有文件列表,并返回相关列表文件。

  3. 获取文件列表后,External 会生成查询计划,完成查询操作和 Hudi、Iceberg 的元数据交互。

  4. HashData 在获取数据后,会将文件列表打包,然后分发给每个 Segment 节点,Segment 会获取文件列表里的一个分片,并依据这些信息读取数据。在数据返回后,整个读取数据的流程就此结束。


结语

Hudi、Iceberg 作为当前主流的数据湖方案,受到广泛青睐。HashData“湖仓一体”技术方案,打通了数据仓库和数据湖,底层支持多种数据类型并存,能够真正实现数据间的相互共享,上层可以通过统一封装的接口进行访问,可同时支持实时查询和分析,为企业在数据湖架构下的数据治理与使用带来了更多的便利。

发布于: 刚刚阅读数: 2
用户头像

还未添加个人签名 2021-03-10 加入

酷克数据是中国领先的云原生数据仓库软件公司,致力以领先技术降低大数据分析的门槛和成本,我们的产品广泛应用于金融、运营商、能源等领域,帮助企业构筑稳定高效、自主可控的数据底座。

评论

发布
暂无评论
HashData的湖仓一体思考:Iceberg、Hudi特性讲解与支持方案_酷克数据HashData_InfoQ写作社区