【hudi学习笔记】hudi基础教程-hudi表设计

本文详细介绍了Hudi表的三个关键组件:有序的时间轴元数据、分层数据文件结构和不同类型的索引。Hudi通过这些设计提供了快速upsert、增量查询和数据一致性等功能,适用于大规模数据湖处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一. hudi表设计

在较高的层次上,用于写Hudi表的组件使用了一种受支持的方式嵌入到Apache Spark作业中,它会在支持DFS的存储上生成代表Hudi表的一组文件。然后,在具有一定保证的情况下,诸如Apache Spark、Presto、Apache Hive之类的查询引擎可以查询该表。Hudi表的三个主要组件:

1)有序的时间轴元数据。类似于数据库事务日志。

2)分层布局的数据文件:实际写入表中的数据。

3)索引(多种实现方式):映射包含指定记录的数据集。

Hudi提供了以下功能来对基础数据进行写入、查询,这使其成为大型数据湖的重要模块:

1)支持快速,可插拔索引的upsert();

2)高效、只扫描新数据的增量查询;

3)原子性的数据发布和回滚,支持恢复的Savepoint;

4)使用mvcc(多版本并发控制)风格设计的读和写快照隔离;

5)使用统计信息管理文件大小;

6)已有记录update/delta的自管理压缩;

7)审核数据修改的时间轴元数据;

8)满足GDPR(通用数据保护条例)、数据删除功能。

 1 时间轴

这个之前讲过,直接上连接

【hudi学习笔记】hudi基础教程-Timeline时间轴_foxofwind的博客-优快云博客

2 数据文件

 Hudi将DFS上的数据集组织到基本路径下的目录结构中。数据集分为多个分区,这些分区是包含该分区的数据文件的文件夹,这与Hive表非常相似。 每个分区被相对于基本路径的特定分区路径区分开来。

在每个分区内,文件被组织为文件组,由文件id唯一标识。 每个文件组包含多个文件切片,其中每个切片包含在某个提交/压缩即时时间生成的基本列文件(*.parquet)以及一组日志文件(*.log*),该文件包含自生成基本文件以来对基本文件的插入/更新。 Hudi采用MVCC设计,其中压缩操作将日志和基本文件合并以产生新的文件片,而清理操作则将未使用的/较旧的文件片删除以回收DFS上的空间。

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

3 索引

Hudi通过索引机制提供高效的upsert操作,该机制会将一个记录键+分区路径组合一致性的映射到一个文件ID.这个记录键和文件组/文件ID之间的映射自记录被写入文件组开始就不会再改变。简而言之,这个映射文件组包含了一组文件的所有版本。Hudi当前提供了3种索引实现(HBaseIndex,、HoodieBloomIndex(HoodieGlobalBloomIndex)、InMemoryHashIndex)来映射一个记录键到包含该记录的文件ID。这将使我们无需扫描表中的每条记录,就可显著提高upsert速度。Hudi索引可以根据其查询分区记录的能力进行分类:

1)全局索引:不需要分区信息即可查询记录键映射的文件ID。比如,写程序可以传入null或者任何字符串作为分区路径(partitionPath),但索引仍然会查找到该记录的位置。全局索引在记录键在整张表中保证唯一的情况下非常有用,但是查询的消耗随着表的大小呈函数式增加。

2)非全局索引:与全局索引不同,非全局索引依赖分区路径(partitionPath),对于给定的记录键,它只会在给定分区路径下查找该记录。这比较适合总是同时生成分区路径和记录键的场景,同时还能享受到更好的扩展性,因为查询索引的消耗只与写入到该分区下数据集大小有关系。

二.hudi文件格式

hudi文件格式,它最底层是基于Fileslice的设计,翻译过来就是文件片,文件片包含基本文件和增量日志文件。基本文件就是一个Parquet或者是ORC文件,增量文件是log文件,对于log文件的写入Hudi里编码了一些block,一批Update可以编码成一个数据块,写到文件里。而基础文件是可插拔,可以基于Parquet,最新的9.0版本已经支持了ORC。还有基于HFile,HFile可用作元数据表。

Log文件里保存了一系列各种各样的数据块,它是有点类似于数据库的重做日志,每个数据版本都可以通过重做日志找到。对于基础文件和Log文件通过压缩做合并形成新的基础文件。Hudi提供了同步和异步的两种方式,这为用户提供了很灵活的选择,比如做可以选择同步Compaction,如果对延迟不敏感,而不需要额外异步起一个作业做Compaction,或者有些用户希望保证写入链路的延迟,可以异步做Compaction而不影响主链路。

 

 Hudi基于File Slice上有个File Group的概念,File Group会包含有不同的File Slice,也File Slice构成了不同的版本,Hudi提供了机制来保留元数据个数,保证元数据大小可控。

对于数据更新写入,尽量使用append,比如之前写了一个Log文件,在更新时,会继续尝试往Log文件写入,对于HDFS这种支持append语义的存储非常友好,而很多云上对象存储不支持append语义,即数据写进去之后不可更改,只能新写Log文件。对于每个文件组也就是不同FileGroup之间是互相隔离的,可以针对不同的文件组做不同的逻辑,用户可以自定义算法实现,非常灵活。

基于Hudi FileGroup的设计可以带来不少收益。比如基础文件是100M,后面对基础文件进行了更新50M数据,就是4个FileGroup,做Compaction合并开销是600M,50M只需要和100M合,4个150M开销就是600M,这是有FileGroup设计。还是有4个100M的文件,也是做了更新,每一次合,比如25M要和400M合并,开销是1200M,可以看到采用FileGroup的设计,合并开销减少一半。

### 如何在 Spark 中创建 Hudi 表 要在 Spark 中创建 Hudi 表,可以通过以下方式实现。以下是具体的操作方法以及需要注意的关键点: #### 1. 配置环境 为了支持 Hudi 的功能,在启动 `spark-shell` 或者提交 Spark 应用程序之前,需要加载必要的依赖项并设置相应的配置参数。 ```bash ./bin/spark-shell \ --master local[*] \ --conf 'spark.serializer=org.apache.spark.serializer.KryoSerializer' \ --conf 'spark.sql.extensions=org.apache.spark.sql.hudi.HoodieSparkSessionExtension' \ --conf 'spark.sql.catalog.spark_catalog=org.apache.spark.sql.hudi.catalog.HoodieCatalog' ``` 如果使用的是 YARN 模式,则可以调整 `--master` 参数为 `yarn`[^2]。 #### 2. 创建 HudiHudi 支持多种存储模式(COPY_ON_WRITE 和 MERGE_ON_READ),下面分别介绍如何基于这两种模式创建表。 ##### (a) COPY_ON_WRITE 模式的建表语句 以下是一个典型的 DDL 建表语句,用于定义一个 COW 类型的 Hudi 表: ```sql CREATE TABLE hudi_cow_table ( id BIGINT, name STRING, price DOUBLE, ts TIMESTAMP, dt STRING ) USING hudi OPTIONS ( type='cow', primaryKey='id', preCombineField='ts' ) PARTITIONED BY (dt); ``` 此语句中的关键选项解释如下: - `type='cow'`: 定义该表采用 Copy-On-Write 存储模型。 - `primaryKey='id'`: 设置主键字段,用于唯一标识每条记录。 - `preCombineField='ts'`: 当存在重复数据时,指定哪个时间戳字段作为最新的依据。 - `PARTITIONED BY (dt)`: 将数据按日期分区存储,便于查询优化[^1]。 ##### (b) MERGE_ON_READ 模式的建表语句 MERGE_ON_READ 是另一种常见的存储模式,适用于频繁更新和增量处理场景。其建表语法类似于 COW,只需更改类型即可: ```sql CREATE TABLE hudi_mor_table ( id BIGINT, name STRING, price DOUBLE, ts TIMESTAMP, dt STRING ) USING hudi OPTIONS ( type='mor', -- 使用 Merge-on-Read 模式 primaryKey='id', preCombineField='ts' ) PARTITIONED BY (dt); ``` 同样地,也可以通过编程接口动态构建 Schema 并写入数据至 Hudi 表中。例如: ```scala import org.apache.spark.sql.SaveMode val df = spark.read.format("csv").option("header", "true").load("/path/to/data.csv") df.write.format("hudi"). options(Map( "hoodie.datasource.write.table.type" -> "COPY_ON_WRITE", "hoodie.datasource.write.precombine.field" -> "ts", "hoodie.datasource.write.recordkey.field" -> "id", "hoodie.datasource.write.partitionpath.field" -> "dt" )). mode(SaveMode.Append).save("/path/to/hudi/table") ``` 这段代码展示了如何利用 DataFrame API 向已存在的 Hudi 表追加新数据[^3]。 #### 3. 解决常见问题 当尝试创建 Hudi 表遇到异常时,可能是因为缺少某些 JAR 文件或未正确注册扩展类。此时可以根据错误提示定位原因,并采取相应措施解决。比如缺失 `HoodieParquetRealtimeInputFormat` 类的情况,需确认是否已经将 `hudi-hadoop-mr-bundle-x.y.z.jar` 添加到了 Hive Metastore 所使用的库路径下[^3]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值