当前位置:   article > 正文

Data Lake 三剑客——Delta、Hudi、Iceberg 对比分析_iceberg hudi

iceberg hudi

共同点

定性上讲,三者均为 Data Lake 的数据存储中间层,其数据管理的功能均是基于一系列的 meta 文件。meta 文件的角色类似于数据库的 catalog/wal,起到 schema 管理、事务管理和数据管理的功能。与数据库不同的是,这些 meta 文件是与数据文件一起存放在存储引擎中的,用户可以直接看到。这种做法直接继承了大数据分析中数据对用户可见的传统,但是无形中也增加了数据被不小心破坏的风险。一旦某个用户不小心删了 meta 目录,表就被破坏了,想要恢复难度非常大。

Meta 文件包含有表的 schema 信息。因此系统可以自己掌握 Schema 的变动,提供 Schema 演化的支持。Meta 文件也有事务(transaction) transaction log 的功能(需要文件系统有原子性和一致性的支持)。所有对表的变更都会生成一份新的 meta 文件,于是系统就有了 ACID 和多版本的支持,同时可以提供访问历史的功能。在这些方面,三者是相同的。下面来谈一下三者的不同。

一、Hudi

        Hudi 的设计目标正如其名,Hadoop Upserts Deletes and Incrementals,强调了其主要支持 Upserts、Deletes 和 Incremental 数据处理,其主要提供的写入工具是 Spark HudiDataSource API 和自身提供的 DeltaStreamer,均支持三种数据写入方式:UPSERT,INSERT 和 BULK_INSERT。其对 Delete 的支持也是通过写入时指定一定的选项支持的,并不支持纯粹的 delete 接口。

其典型用法是将上游数据通过 Kafka 或者 Sqoop,经由 DeltaStreamer 写入 Hudi。DeltaStreamer 是一个常驻服务,不断地从上游拉取数据,并写入 hudi。写入是分批次的,并且可以设置批次之间的调度间隔。默认间隔为 0,类似于 Spark Streaming 的 As-soon-as-possible 策略。随着数据不断写入,会有小文件产生。对于这些小文件,DeltaStreamer 可以自动地触发小文件合并的任务。

在查询方面,Hudi 支持 Hive、Spark、Presto。

在性能方面,Hudi 设计了 HoodieKey,一个类似于主键的东西。HoodieKey有 Min/Max 统计,BloomFilter,用于快速定位 Record 所在的文件。在具体做 Upserts 时,如果 HoodieKey不存在于 BloomFilter,则执行插入,否则,确认 HoodieKey是否真正存在,如果真正存在,则执行 update。这种基于 HoodieKey + BloomFilter 的 upserts 方法是比较高效的,否则,需要做全表的 Join 才能实现 upserts。对于查询性能,一般需求是根据查询谓词生成过滤条件下推至 datasource。Hudi 这方面没怎么做工作,其性能完全基于引擎自带的谓词下推和 partition prune 功能。

Hudi 的另一大特色是支持 Copy On Write 和 Merge On Read。前者在写入时做数据的 merge,写入性能略差,但是读性能更高一些。后者读的时候做 merge,读性能查,但是写入数据会比较及时,因而后者可以提供近实时的数据分析能力。

最后,Hudi 提供了一个名为 run_sync_tool 的脚本同步数据的 schema 到 Hive 表。Hudi 还提供了一个命令行工具用于管理 Hudi 表。

hudi

二、Iceberg

Iceberg 是什么?

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 的文件的组织形式。

如下图所示,Iceberg 文件组织分为四层,分别为Metadata、Snapshot、Manifest、File。

  • Metadata 文件:该文件记录了最新的快照信息和历史的快照记录。并且记录了最新的 Schema 信息。
  • Snapshot 文件(图中Snap-x):由于 Iceberg 基于 MVCC(多版本并发控制) 的设计理念,每次 Commit 都会生成一个 Snapshot, 该 Snapshot 是当时表的全局快照,即选定某个快照读取时,读到的是全量数据。Snapshot 文件记录了历史的 Manifest 文件和本次 Commit 新增的 Manifest,当我们增量读取时,只需要读取指定快照的新增的 Manifest 就可以实现读取新增的数据。
  • Manifest 文件(图中mx):该文件记录了本次事务中写入的文件和分区的对应关系,并且记录了文件中字段的一些统计信息(如最大值、最小值)以便于快速查找。
  • File:实际写入的数据文件,如 Parquet、Avro 等格式文件。

三、事务性

Iceberg 的一个亮点是提供了 ACID 的语义支持,通过 Snapshot 进行读写分离,提供了 Serializable isolation,且所有的操作都可以保证原子性。

相比 Hive 而言,Iceberg 提供的事务性可以隔离写入任务队分析任务的不利影响,且写入失败不会出现脏数据。更加吸引人的是 Iceberg 和 Flink 的结合,通过 Flink 的 Checkpoint 机制和 Iceberg 的事务性,可以做到端到端的 Exactly once 语义。

六、行级更新
Iceberg 表的一大亮点是提供了 OLAP 场景下列式存储数据集的更新和删除的能力。由于 Iceberg 表支持更新和删除,因此你可以将 Changelog (如 Binlog 数据) 导入到 Iceberg 表中进行分析,同时也可以操作 Iceberg 表以更新或删除某一行或者一批数据。而在 Hive 中,由于 Hive 不支持更新,因此每次只能全量写入,浪费了计算资源,且存在较多的冗余数据。

Iceberg 表的更新是采用 MOR (Merge on Read) 模式来实现的,任何的更新和删除操作并不会对原数据文件进行操作,而是采用追加的形式写入到另一个文件中(Delete File),在读取时进行 Merge 操作以获取正确的结果。

 

在查询方面,Iceberg 支持 Spark、Presto。

        Iceberg 在查询性能方面做了大量的工作。值得一提的是它的 hidden partition 功能。Hidden partition 意思是说,对于用户输入的数据,用户可以选取其中某些列做适当的变换(Transform)形成一个新的列作为 partition 列。这个 partition 列仅仅为了将数据进行分区,并不直接体现在表的 schema 中。例如,用户有 timestamp 列,那么可以通过 hour(timestamp) 生成一个 timestamp_hour 的新分区列。timestamp_hour 对用户不可见,仅仅用于组织数据。Partition 列有 partition 列的统计,如该 partition 包含的数据范围。当用户查询时,可以根据 partition 的统计信息做 partition prune。

        除了 hidden partition,Iceberg 也对普通的 column 列做了信息收集。这些统计信息非常全,包括列的 size,列的 value count,null value count,以及列的最大最小值等等。这些信息都可以用来在查询时过滤数据。

        Iceberg 提供了建表的 API,用户可以使用该 API 指定表明、schema、partition 信息等,然后在 Hive catalog 中完成建表。

三、Delta

我们最后来说 Delta。Delta 的定位是流批一体的 Data Lake 存储层,支持 update/delete/merge。由于出自 Databricks,spark 的所有数据写入方式,包括基于 dataframe 的批式、流式,以及 SQL 的 Insert、Insert Overwrite 等都是支持的(开源的 SQL 写暂不支持,EMR 做了支持)。与 Iceberg 类似,Delta 不强调主键,因此其 update/delete/merge 的实现均是基于 spark 的 join 功能。在数据写入方面,Delta 与 Spark 是强绑定的,这一点 Hudi 是不同的:Hudi 的数据写入不绑定 Spark(可以用 Spark,也可以使用 Hudi 自己的写入工具写入)。

在查询方面,开源 Delta 目前支持 Spark 与 Presto,但是,Spark 是不可或缺的,因为 delta log 的处理需要用到 Spark。这意味着如果要用 Presto 查询 Delta,查询时还要跑一个 Spark 作业。更为蛋疼的是,Presto 查询是基于 SymlinkTextInputFormat。在查询之前,要运行 Spark 作业生成这么个 Symlink 文件。如果表数据是实时更新的,意味着每次在查询之前先要跑一个 SparkSQL,再跑 Presto。这样的话为何不都在 SparkSQL 里搞定呢?这是一个非常蛋疼的设计。为此,EMR 在这方面做了改进,支持了 DeltaInputFormat,用户可以直接使用 Presto 查询 Delta 数据,而不必事先启动一个 Spark 任务。

在查询性能方面,开源的 Delta 几乎没有任何优化。Iceberg 的 hidden partition 且不说,普通的 column 的统计信息也没有。Databricks 对他们引以为傲的 Data Skipping 技术做了保留。不得不说这对于推广 Delta 来说不是件好事。EMR 团队在这方面正在做一些工作,希望能弥补这方面能力的缺失。

Delta 在数据 merge 方面性能不如 Hudi,在查询方面性能不如 Iceberg,是不是意味着 Delta 一无是处了呢?其实不然。Delta 的一大优点就是与 Spark 的整合能力(虽然目前仍不是很完善,但 Spark-3.0 之后会好很多),尤其是其流批一体的设计,配合 multi-hop 的 data pipeline,可以支持分析、Machine learning、CDC 等多种场景。使用灵活、场景支持完善是它相比 Hudi 和 Iceberg 的最大优点。另外,Delta 号称是 Lambda 架构、Kappa 架构的改进版,无需关心流批,无需关心架构。这一点上 Hudi 和 Iceberg 是力所不及的。

delta

总结

通过上面的分析能够看到,三个引擎的初衷场景并不完全相同,

  • Hudi 为了 incremental 的 upserts,
  • Iceberg 定位于高性能的分析与可靠的数据管理,
  • Delta 定位于流批一体的数据处理。

这种场景的不同也造成了三者在设计上的差别。尤其是 Hudi,其设计与另外两个相比差别更为明显。随着时间的发展,三者都在不断补齐自己缺失的能力,可能在将来会彼此趋同,互相侵入对方的领地。当然也有可能各自关注自己专长的场景,筑起自己的优势壁垒,因此最终谁赢谁输还是未知之数。

下表从多个维度对三者进行了总结,需要注意的是此表所列的能力仅代表至 2019 年底。

DeltaHudiIceberg
Incremental IngestionSparkSparkSpark
ACID updatesHDFS, S3 (Databricks), OSSHDFSHDFS, S3
Upserts/Delete/Merge/UpdateDelete/Merge/UpdateUpserts/DeleteNo
Streaming sinkYesYesYes(not ready?)
Streaming sourceYesNoNo
FileFormatsParquetAvro,ParquetParquet, ORC
Data SkippingFile-Level Max-Min stats + Z-Ordering (Databricks)File-Level Max-Min stats + Bloom FilterFile-Level Max-Min Filtering
Concurrency controlOptimisticOptimisticOptimistic
Data ValidationYes (Databricks)NoYes
Merge on readNoYesNo
Schema EvolutionYesYesYes
File I/O CacheYes (Databricks)NoNo
CleanupManualAutomaticNo
CompactionManualAutomaticNo

网易数帆技术选型

在最早做技术选型的时候,团队也调研过与 Iceberg 同类型的开源项目 Apache Hudi 和 Delta Lake,但最终都因为一些原因而放弃选用。在做调研时,Hudi 还是相对比较封闭的状态(它对自己的定位是 Spark 的一个 Lib,去年年底到今年才开始真正把支持 Flink 作为优先级比较高的工作),而网易数帆需要一个开放的解决方案来适配高度定制化需求。

除此之外,也有一些技术细节的考量。比如数据格式方面的问题,Hudi 的文件索引采用的是 Bloomfilter 以及 HBase 的机制,这两种机制都不是特别理想,HBase 需要引入第三方 KV 数据库,对商业输出不利,而 Bloomfilter 比较重,会让实时性大打折扣,因此都不太适合网易数帆的技术选型。网易数帆对 Arctic 核心功能的想法和设计,也跟 Hudi 有出入。

而没选 Delta Lake 则是因为它对实时性并不是看得很重,马进团队通过研究相关论文发现,Delta Lake 更多还是把 Spark 的生态作为第一优先级,这与团队做湖仓一体的目标还是有一些区别。

相比之下,Iceberg 相对更开放,对计算引擎的集成、对上层元数据的集成、对不同系统的集成都做得比较好,可以满足团队高度定制化的需求。因此团队最终选择了 Iceberg,能更好地落实自己的想法,并做出网易数帆独有的功能特色。

基于 Iceberg,但不局限于 Iceberg

虽然 Arctic 以 Iceberg 为底座,但从社区定位来看,Hudi 才是跟 Arctic 最像的。数据湖仓有一个非常重要的功能,即能够基于主键进行行级更新,Hudi 在功能上与 Arctic 比较匹配,只是在核心设计上二者存在分歧,在实时入湖这一方面 Hudi 也最具有代表性。所以 Arctic 在做性能对比测试的时候,也是拿 Hudi 来对比,而不是 Iceberg。

实际上,网易数帆团队在一开始做 Arctic 这个产品时,并不打算绑定任何一个开源的数据湖方案,包括 Iceberg。

最初团队更希望基于数据湖做一个流批一体的湖仓,通过制定一个管理 Base 数据(即存量数据)和 Change 数据(即增量数据或实时数据)的方案,做到对两种数据的解耦,不管底层使用什么数据湖技术,无论 Iceberg 还是 Delta Lake,对外暴露的都是同一套湖仓一体方案。这是 Arctic 最初的定位,即不跟任何一家数据湖基座做高度绑定,但要做到这一点需要极高的研发投入,很难一步到位。因此前期团队对于 Arctic 的定位首先是满足网易湖仓一体的业务目标,把上层实时入湖功能涉及的读合并、异步合并、元数据服务、小文件治理等等在一个数据湖基座上管理起来,有了数据湖的基座,就可以基于此再去做上层的服务,然后再考虑增加在不同数据湖上构建湖仓一体的能力。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/592702
推荐阅读
相关标签
  

闽ICP备14008679号