暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

【翻译】Apache Hudi 重要的概念和术语

kpxiaoxm 2021-06-29
729

Apache Hudi 重要的概念和术语

Apache Hudi(发音为“Hudi”)在hadoop兼容的存储上提供以下流原语:

  • Update/Delete Records 更新删除记录 (如何更改表中的记录?)

  • Change Streams 改变流 (我如何获取改变了的记录?)

在本节中,我们将讨论重要的概念和术语,这些概念和术语有助于理解并有效使用这些原语。

时间轴(Timeline)

在其核心,Hudi维护了一个时间轴(Timeline
),记录了在不同时间瞬间(instants
)在表上执行的所有操作,这有助于提供表的瞬时视图,同时还有效地支持按到达顺序检索数据。
一个 Hudi instant 由以下组件组成:

  • Instant action
     :在表上执行的操作类型

  • Instant time
     :通常是一个时间戳(例如:20190117010349),该时间戳按操作开始时间的顺序单调增加。

  • state
     :当前 Instant 的状态

Hudi保证在时间轴上执行的操作的原子性和基于 Instant time 的时间轴一致性;
执行的关键操作包括:

  • COMMITS
     - 一次提交表示将一组记录原子写入到表中。

  • CLEANS
     - 删除表中不再需要的旧文件版本的后台活动。

  • DELTA_COMMIT
     - 增量提交是指将一批记录原子写入到MergeOnRead存储类型的表中,其中一些/所有数据都可以只写到增量日志中。

  • COMPACTION
     - 协调Hudi中差异数据结构的后台活动,例如:将更新从基于行的日志文件变成列格式。在内部,压缩表现为时间轴上的特殊提交。

  • ROLLBACK
     - 表示提交/增量提交不成功且已回滚,删除在写入过程中产生的所有部分文件。

  • SAVEPOIN
    T - 将某些文件组标记为“已保存”,以便清理程序不会将其删除。在发生灾难/数据恢复的情况下,它有助于将表还原到时间轴上的某个点。

任何给定的即时(Instant)都可以处于以下状态之一:

  • REQUESTED
     - 表示已调度但尚未启动的操作。

  • INFLIGHT
     - 表示当前正在执行该操作。

  • COMPLETED
     - 表示在时间轴上完成了该操作。

通过官方例子理解Timeline

上面的示例显示了在Hudi表上大约10:00到10:20之间发生的更新事件,大约每5分钟一次,将提交元数据以及其他后台清理/压缩保留在Hudi时间轴上。
观察的关键点是:提交时间(commit time)指示数据的到达时间(arrival time
)(上午10:20),而实际数据组织则反映了实际时间(actual time)或事件时间(event time
),即数据所反映的(从07:00开始的每小时时段)。在权衡数据延迟和完整性时,这是两个关键概念(arrival time 和 event time )。
如果有延迟到达的数据(事件时间为9:00的数据在10:20达到,延迟 >1 小时),我们可以看到upsert将新数据生成到更旧的时间桶(buckets)/文件夹(folders)中。在时间轴的帮助下,增量查询可以只提取10:00以后成功提交的新数据,并非常高效地只消费更改过的文件,且无需扫描更大的文件范围,例如07:00后的所有时间段。

文件管理(File management)

Hudi将一个表组织成 DFS 基本路径(basepath
) 下的目录结构。表被划分为多个分区,这些分区是包含该分区数据文件的文件夹,与Hive表非常相似。每个分区都由它的分区路径(partitionpath
)唯一标识,该路径相对于basepath。
在每个分区中,文件被组织成文件组(file groups
),由文件id(file id
)唯一标识。每个文件组包含多个文件切片(file slices
),其中每个切片包含在某个提交/压缩瞬间生成的基本文件(*.parquet
),以及一组日志文件(*.log.*
),这些日志文件包含自生成基本文件以来对基本文件的插入/更新。Hudi采用MVCC设计,压缩(COMPACTION)操作将日志和基本文件合并,生成新的文件切片,清理(CLEANS)操作将删除未使用的/旧的文件切片,回收DFS上的空间。

索引(Index)

Hudi通过索引机制,通过将给定的帽衫键(hoodie key
)(记录键+分区路径)一致地映射到文件id,从而提供了高效的更新。一旦记录的第一个版本被写入文件,记录键和文件组/文件id之间的映射就不会改变。简而言之,映射文件组包含一组记录的所有版本。

表类型和查询(Table Types & Queries)

Hudi表类型定义了数据如何在DFS上建立索引和布局,以及如何在这样的组织上实现上述原语和时间轴活动(即如何写入数据)。
反过来,查询类型定义如何将基础数据公开给查询(即如何读取数据)。

表类型支持的查询类型
写时复制 (Copy On Write)快照查询+增量查询
读时合并 (Merge On Read)快照查询+增量查询+读取优化查询

表类型(Table Types)

Hudi支持以下表类型。

  • Copy On Write :仅使用列文件格式(例如parquet)存储数据。通过在写入过程中执行同步合并以更新版本并重写文件。

  • Merge On Read :使用列式(例如parquet)+ 基于行(例如avro)的文件格式组合来存储数据。更新记录到增量文件中,然后进行同步或异步压缩以生成列文件的新版本。

下表总结了这两种表类型之间的权衡

权衡写时复制(Copy On Write)读时合并(Merge On Read)
数据延迟更高更低
更新成本(I/O)更高(重写整个parquet文件)更低(追加到增量日志)
Parquet文件大小更小(高更新成本(I/O))更大(低更新成本)
写放大更高更低(取决于压缩策略)

查询类型(Query types)

Hudi支持以下查询类型:

  • 快照查询:查询将看到给定提交或压缩操作时该表的最新快照。如果是merge on read表,它通过动态合并最新文件片的基本文件和增量文件来公开接近实时的数据(几分钟内);如果是copy on write表,它提供了现有parquet表的直接替换品,同时提供了upsert/delete和其他写端特性。

  • 增量查询:增量查询只能看到从给定的提交/压缩(commit/compaction)时间以来写入表的新数据。这有效地提供了变更流来启用增量数据管道。

  • 读取优化查询:查询将看到给定提交/压缩操作时表的最新快照。仅公开最新文件片中的基本/列文件,并保证与非hudi列式表相比具有相同的列式查询性能。

下表总结了不同查询类型之间的权衡:

权衡快照读优化
数据延迟更低更高
查询延迟更高(合并基本/列式文件+基于行的增量日志文件)更低(原始的基本/列式文件性能)

写时复制表(Copy On Write Table)

写时复制表中的文件切片只包含基本文件/列式文件,并且每次提交都会生成基本文件的新版本。换句话说,我们隐式地压缩每个提交,以便只存在列式数据。因此,写入放大(为传入数据的1字节写入的字节数)要高得多,其中读取放大为零。这是分析工作负载非常需要的属性,分析工作负载主要是大量读取。
下面从概念上说明了如何将数据写入写入copy-on-write表以及在其上运行的两个查询。
以下内容从概念上说明了将数据写入写时复制表(copy-on-write table)并在其上运行两个查询时,它是如何工作的。

在写入数据时,对现有文件组的更新
将为该文件组生成一个带有提交即时时间标记的新切片,而插入
分配一个新文件组并写入该文件组的第一个切片。这些文件切片及其提交即时时间在上面用颜色编码。针对这样一个表运行的SQL查询(例如:select count(*)
计算该分区中的记录总数),首先检查最新提交的时间轴,并筛选每个文件组中除了最新的文件片之外的所有文件片。如您所见,旧的查询不会看到以粉红色编码的当前进行中的提交的文件,但是在该提交后的新查询会获取新数据。因此,查询不受任何写失败/部分写的影响,只在提交的数据上运行。
写时复制表的目的是是为了从根本上改进现在管理表的方式:

  • 优先支持在文件级原子更新数据,而无需重写整个表/分区

  • 增量消费更改的能力,而不是低效的扫描或试探性的摸索

  • 严格控制文件大小以保持优异的查询性能(小文件严重影响查询性能)。

读时合并表(Merge On Read Table)

读时合并表是写时复制的超集,因为它仍然支持通过在最新的文件切片中仅公开基本/列式文件来支持对表的读优化查询。
此外,它将每个文件组传入的插入更新存储在基于行的增量日志中,以便通过在查询期间动态地将应用有最新版本文件ID的增量日志来支持快照查询。
因此,此表类型试图智能地平衡读和写放大,以提供接近实时的数据。
这里最重要的变化是压缩器,它现在仔细选择需要将哪些增量日志文件压缩到它们的列式基本文件上,以保持查询性能处于受控状态(较大的增量日志文件会导致查询端合并数据的合并时间更长)
下面说明了表的工作方式,并显示了两种类型的查询 - 快照查询和读取优化查询。

在这个示例中发生了许多有趣的事情,这些事情展示了这种方法的微妙之处。

  • 现在,我们大约每1分钟就有一次提交,这在其他表类型中是无法做到的。

  • 现在,在每个文件id组中,都有一个增量日志文件,用于保存基本列式文件中记录的更新。在本例中,增量日志文件保存了从10:05到10:10的所有数据。与以前一样,基本列式文件仍然使用提交(commit)进行版本控制。因此,如果只查看基本文件,那么表布局看起来就像写时复制表的副本。

  • 定期压缩过程会从增量日志中合并这些更改,并生成一个新版本的基本文件,就像示例中在10:05时发生的情况一样。

  • 查询同一基础表有两种方法:读优化查询和快照查询,取决于我们选择的是查询性能还是数据新鲜度。

  • 对于读优化查询,关于提交数据何时可用的语义会以一种微妙的方式发生变化。注意,这样一个在10:10运行的查询不会看到上面10:05之后的数据,而快照查询总是看到最新的数据。

  • 何时触发压缩以及压缩什么是解决这些难题的关键。通过实施压缩策略,我们将最新的分区与旧的分区进行了比较,我们可以确保读取优化的查询在X分钟内以一致的方式查看已发布的数据。

读时合并表的目的是在DFS上直接支持接近实时的处理,而不是将数据复制到可能无法处理该数据量的专门系统。这个表还有一些次要的好处,比如避免同步合并数据,从而减少了写入放大,即批处理中每1字节数据写入的数据量。


文章转载自kpxiaoxm,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论