探索Apache Hudi核心概念 (1) - File Layouts

简介: 探索Apache Hudi核心概念 (1) - File Layouts

在以往众多介绍Apcache Hudi的文章中,对核心概念的解读大多会引用官方文档中的概念图,像Timeline(时间线)、File Layouts(文件布局)这类结构清晰,逻辑严谨的概念,图解是很好的说明方式。但是,抽象概念与实际运行状况还是有不少差异的,相信很多学习和使用Hudi的开发者都曾尝试过:将文档中的概念和Hudi的实际运行状况结合起来推导每个动作背后的逻辑是什么。这个过程非常有意义,因为它可以帮助我们透彻地理解Hudi的工作原理。

在这方面,Notebook是一个绝佳的工具,我们可以利用Notebook良好的交互能力,设计一系列有针对性的操作,让这些操作去触发Hudi的某些机制,然后观察Hudi数据集的状态(包括元数据和存储文件),再结合对Hudi相关概念的介绍,解读这些行为。利用Notebook提供的统一环境和上下文,我们可以非常生动地观察到Hudi的运行机制,并深刻体会到其背后的控制原理,这也正是本系列文章的写作灵感:我们希望借助Notebook“探索,发现,思考,领悟”的思维模式,带领大家开启一段Hudi核心概念的探索之旅。

1. Notebook 介绍

本系列文章配备一组专门开发的Notebook,文章的讲解与Notebook演示紧密结合,Notebook项目地址如下:

项目名称 项目地址
Apache Hudi Core Conceptions https://github.com/bluishglc/apache-hudi-core-conceptions

Notebook的运行环境使用的是Amazon EMR Studio(一种面向Amazon EMR的托管Notebook环境),如果您没有AWS账号,可以自行修改Notebook适配到任何支持Spark Kernel的Notebook环境中。Notebook还使用了一个公共数据集:Amazon Customer Reviews,它是Amazon购物网站上的用户评价数据,总体积50GB,存放在S3上,地址:s3://amazon-reviews-pds^1[1],如果您没有AWS账号,可以通过S3客户端工具下载到本地使用。Notebook使用的Hudi版本是0.12.1,Spark集群建议配置:32 vCore / 128 GB及以上。

为了更好地演示Hudi的特性,我们对原始数据做了一些必要的裁剪,并将裁剪工作封装成一个独立的Notebook:《Apache Hudi Core Conceptions (1) - Data Preparation》[2],对应文件:1-data-preparation.ipynb,这个notebook主要完成两项工作:

  1. 1. 使用Spark SQL创建外部表:all_reviews,location指向 s3://amazon-reviews-pds,便于通过数据表的形式读取原始数据
  2. 2. 使用Spark SQL创建正式的源数据表:reviews,该表在all_reviews基础上做了如下裁剪:

• 为保证所有记录大小基本一致^2[3],仅选取review_id, star_rating, review_body, review_date和year五个必要字段,同时将长短不一的review_id和review_body两个字段的值使用定长的uuid字符串替换

• 为便于按年份读取数据,用year重新进行了分区

其他的Notebook均以测试用例的形式组织,每个用例都会创建用例专属的数据表,并根据测试意图配置特定的Hudi属性。这些数据表的结构与数据准备时创建的revieiws表基本一致,只是多了Hudi需要的preCombineField列timestamp和为演示特别设计的分区列parity。parity是基于review_id的crc32编码对2取余后得到的数值0或1,设计该列作为分区列的考量是:保证测试表即不会没有分区,这样说明性会不足,又不会有太多分区,这样会干扰解读,所以使用这种只有0、1两种取值的列作分区是非常适合的。

提示:运行任何Notebook前,请先至少执行一次《Apache Hudi Core Conceptions (1) - Data Preparation》[4],以确保源数据表reviews被创建。所有Notebook都定义了一个环境变量:S3_BUCKET,需要您将其修改为自己的S3桶。

2. 运行 Notebook

本文会使用到两个Notebook:《Apache Hudi Core Conceptions (2) - COW: File Layouts & File Sizing》[5]《Apache Hudi Core Conceptions (3) - MOR: File Layouts & File Sizing》[6],对应文件分别是:2-cow-file-layouts-file-sizing.ipynb 和 3-mor-file-layouts-file-sizing.ipynb。运行前,请先修改Notebook中的环境变量S3_BUCKET,将其设为您自己的S3桶,并确保用于数据准备的Notebook:《Apache Hudi Core Conceptions (1) - Data Preparation》[7]已经至少执行过一次。本文,我们只关注两个Notebook中和COW表以及MOR表文件布局有关的内容,对于运行过程中发生的File Sizing和Compaction动作会在后续文章中专门解读。

3. 核心概念

File Layouts(文件布局)是指Hudi的数据文件在存储介质上的分布,Hudi会严格管理数据文件的命名、大小和存放位置,并会在适当时机新建、合并或分裂数据文件,这些逻辑都会体现在文件布局上。

Hudi在文件操作上有一个重要“原则”:Hudi always creates immutable files on disk。即:文件一旦创建,永远不会再更新,任何添加、修改或删除操作只会在现有文件数据的基础上合并输入数据一起写入到下一个新文件中,清楚这一点对我们解读文件布局很有帮助。

整体上,Hudi文件布局的顶层结构是数据表对应的base目录,下一层是各分区目录,分区目录可根据分区列数量嵌套多层,这和Hive/Spark的表结构是一致的。在最底层分区文件夹上,Hudi不再创建子文件夹,全部都是平铺的数据文件,但是这些文件在逻辑上依然有着清晰的层级关系。顶层的文件集合是File Group,File Group下面是File Slice,File Slice下面就是具体的数据文件了。我们反过来,按从下到上的顺序梳理一下这些文件和文件集合:

• Base File

Base File是存储Hudi数据集的主体文件,以Parquet等列式格式存储,所以我们在Hudi中看到的Parquet文件基本都是Base File。实际上,Base File的命名是为了呼应Log File,在没有Log File的COW表里,Base File就是基层的数据存储文件,没必要强调它的“Base”身份,直接叫Parquet文件就可以。Base File遵循一致的命名规范,格式为:

__.parquet

以下是一个真实的Base File文件名:

fileId部分是一个uuid,我们会在多个文件中看到相同的fileId,这些fileId相同的文件就组成了一个File Group。instantTime是写入这个文件对应的instant的时间,也是该文件的一个“版本”标注,因为一个Base File历经多轮增删改操作后就会产生多个版本,Hudi就使用instantTime对它们进行标识。不管是MOR表还是COW表,都有Base File,只是在COW表里只有Base File,在MOR表里除了Base File还有Log File。

• Log File

Log File是在MOR表中用于存储变化数据的文件,也常被称作Delta Log,Log File不会独立存在,一定会从属于某个Parquet格式的Base File,一个Base File和它从属的若干Log File所构成的就是一个File Slice。Log File也遵循一致的命名规范,格式为:

._.log._

以下是一个真实的Log File文件名:

不同于Base File,Log File文件名中时间戳部分并不是Log File自己对应的instanceTime,而是它所从属的Base File的instanceTime,即baseCommitTime。如此一来,就没有办法通过时间戳来区分Log File提交的先后顺序了,所以Hudi在Log File文件名中加入了fileVersion,它是一个从1开始单调递增的序列号,用于标识Log File产生的顺序。

• File Slice

在MOR表里,由一个Base File和若干从属于它的Log File组成的文件集合被称为一个File Slice。应该说File Slice是针对MOR表的特定概念,对于COW表来说,由于它不生成Log File,所以File Silce只包含Base File,或者说每一个Base File就是一个独立的File Silce。总之,对于COW表来说没有必要区分File Silce,也不没必要强调Base File的“Base”身份,只是为了概念对齐,大家会统一约定Hudi文件的三层逻辑布局为:File Group -> File Slice -> Base / Log Files。

• File Group

在前面介绍Base File时,我们已经提到了File Group,简单说,就是fileId相同的文件属于同一个File Group。同一File Group下往往有多个不同版本(instantTime)的Base File(针对COW表)或Base File + Log File的组合(针对MOR表),当File Group内最新的Base File迭代到足够大( >100MB)时,Hudi就不会在当前File Group上继续追加数据了,而是去创建新的File Group。

4. COW表的File Layouts

运行《Apache Hudi Core Conceptions (2) - COW: File Layouts & File Sizing》[8]的第1个测试用例,可以在2.8节看到类似下图这样一个经由实际操作产生的COW表的文件布局:

图中:青色、紫色和红色标注的文件分属于三个File Group,因为它们开头的uuid是三个不同的值,从文件名尾部的instantTime可以推断它们被创建的先后时间。当前的文件布局是历经4次操作演进而来,以分区parity=0为例,时间线(Timeline)如下:

①:第1次插入96M数据,生成了第一个Parquet文件;

②:第2次插入14M数据,在Copy On Write机制运作下,插入的14M数据与原96M数据合并写入新的Parquet文件,大小110M,fileID不变;

③:第3次插入3.7M数据,由于现有Parquet文件已经超过了100M的阈值,被Hudi判定为大文件,故不再选择它进行Copy On Write操作,转而生成新的fileId,创建新的File Group,并将数据写入新File Group的Parquet文件中,大小3.7M;

④:第4次插入182M数据,现有3.7M的文件是一个小文件,Hudi会选择以该文件为基础,将其3.7M的数据和新插入数据一起合并写入新的Parquet文件,由于这次插入的数据量较大,写入文件的体积将会超过Hudi规定的单一Parquet文件的上限(120M),所以Hudi将182M中的116M与现有3.7M数据合并,写满一个Parquet文件(120M),同时创建第3个File Group,将另外66M数据写入第3个File Group下的Parquet文件中。注意:66M的文件使用红色标记,属于一个独立的File Group,但它却是第④次提交的产物,不存在第⑤次提交

关于整条时间线上发生的与File Sizing有关的细节我们会在后续文章中单独介绍,本文先聚焦文件布局本身。

5. MOR表的File Layouts

运行《Apache Hudi Core Conceptions (3) - MOR: File Layouts & File Sizing》[9]的第1个测试用例后,可以在2.8节看到类似下图这样一个经由实际操作产生的MOR表的文件布局:

图中:单一分区下的所有文件属于一个File Group(uuid全部相同),黄色和蓝色标注的文件分属于两个不同的File Slice,其中两个Parquet文件是两个File Slice各自的Base File,它们各带两个Log File。当前的文件布局是历经4次操作(5次提交)演进而来,以分区parity=0为例,时间线(Timeline)如下:

①:第1次插入96M数据,生成了第一个Base File;

②:第2次更新了其中的一小部分数据,生成了从属于第一个Base File文件的第一个Log File,大小804K,fileVersion是1;

③:第3次又更新了其中的一小部分数据,生成了从属于第一个Base File文件的第二个Log File,大小1.2M,fileVersion是2;

④:由于该表启用了同步压缩(Inline Compaction),并将触发Compaction的deltacommits阈值设为了3,所以第3次提交后触发了同步的Compaction操作,Hudi将此前的Base File和两个Log File压缩成一个新的Base File,大小96M,fileId不变,这样就出现了第二个File Slice。每次Compaction都会进行一次独立的提交(即commit,非deltacommit),所以新Base File尾部的时间戳更新为Compaction这次提交对应的instantTime;

⑤:第4次更新(但却已是第5次提交)的数据量达到了307M,由于该测试表的Log File上限被设定为了250M,此次提交触发了Log File的分裂,Hudi将更新数据写入了两个Log File,一个268M,fileVersion是1;另一个39M,fileVersion是2

关于整条时间线上发生的与File Sizing和Compaction有关的细节我们会在后续文章中单独介绍,本文先聚焦文件布局本身。

目录
相关文章
|
3月前
|
消息中间件 存储 负载均衡
Apache Kafka核心概念解析:生产者、消费者与Broker
【10月更文挑战第24天】在数字化转型的大潮中,数据的实时处理能力成为了企业竞争力的重要组成部分。Apache Kafka 作为一款高性能的消息队列系统,在这一领域占据了重要地位。通过使用 Kafka,企业可以构建出高效的数据管道,实现数据的快速传输和处理。今天,我将从个人的角度出发,深入解析 Kafka 的三大核心组件——生产者、消费者与 Broker,希望能够帮助大家建立起对 Kafka 内部机制的基本理解。
125 2
|
7月前
|
SQL 分布式计算 Apache
Apache Doris + Apache Hudi 快速搭建指南|Lakehouse 使用手册(一)
本文将在 Docker 环境下,为读者介绍如何快速搭建 Apache Doris + Apache Hudi 的测试及演示环境,并对各功能操作进行演示,帮助读者快速入门。
Apache Doris + Apache Hudi 快速搭建指南|Lakehouse 使用手册(一)
|
7月前
|
easyexcel Java Apache
EasyExcel导入的时候报错Caused by: java.lang.NoClassDefFoundError: org/apache/poi/poifs/filesystem/File
EasyExcel导入的时候报错Caused by: java.lang.NoClassDefFoundError: org/apache/poi/poifs/filesystem/File
550 0
|
8月前
|
消息中间件 Java Kafka
实时计算 Flink版操作报错合集之从hudi读数据,报错NoSuchMethodError:org.apache.hudi.format.cow.vector.reader.PaequetColumnarRowSplit.getRecord(),该怎么办
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
150 0
|
9月前
|
存储 SQL 分布式计算
使用Amazon EMR和Apache Hudi在S3上插入,更新,删除数据
使用Amazon EMR和Apache Hudi在S3上插入,更新,删除数据
253 0
|
9月前
|
存储 分布式计算 Hadoop
一文了解Apache Hudi架构、工具和最佳实践
一文了解Apache Hudi架构、工具和最佳实践
1586 0
|
9月前
|
SQL 分布式计算 NoSQL
使用Apache Hudi和Debezium构建健壮的CDC管道
使用Apache Hudi和Debezium构建健壮的CDC管道
94 0
|
2月前
|
存储 人工智能 大数据
The Past, Present and Future of Apache Flink
本文整理自阿里云开源大数据负责人王峰(莫问)在 Flink Forward Asia 2024 上海站主论坛开场的分享,今年正值 Flink 开源项目诞生的第 10 周年,借此时机,王峰回顾了 Flink 在过去 10 年的发展历程以及 Flink社区当前最新的技术成果,最后展望下一个十年 Flink 路向何方。
394 33
The Past, Present and Future of Apache Flink
|
4月前
|
SQL Java API
Apache Flink 2.0-preview released
Apache Flink 社区正积极筹备 Flink 2.0 的发布,这是自 Flink 1.0 发布以来的首个重大更新。Flink 2.0 将引入多项激动人心的功能和改进,包括存算分离状态管理、物化表、批作业自适应执行等,同时也包含了一些不兼容的变更。目前提供的预览版旨在让用户提前尝试新功能并收集反馈,但不建议在生产环境中使用。
1023 13
Apache Flink 2.0-preview released
|
4月前
|
存储 缓存 算法
分布式锁服务深度解析:以Apache Flink的Checkpointing机制为例
【10月更文挑战第7天】在分布式系统中,多个进程或节点可能需要同时访问和操作共享资源。为了确保数据的一致性和系统的稳定性,我们需要一种机制来协调这些进程或节点的访问,避免并发冲突和竞态条件。分布式锁服务正是为此而生的一种解决方案。它通过在网络环境中实现锁机制,确保同一时间只有一个进程或节点能够访问和操作共享资源。
172 3

热门文章

最新文章

推荐镜像

更多