标题:复制写入表
权重:43
Hudi将数据组织在基本路径下的目录结构中。在每个分区中,文件被组织成由文件ID唯一标识的组。
每个文件组包含几个文件切片,在COW表的情况下,这些文件切片是表示该文件组不同版本的Parquet文件。
Parquet文件名符合以下结构:[文件ID][文件写入令牌][提交时间戳].[文件扩展名],其中:
- 文件ID - 唯一标识表中的基本文件。基本文件的多个版本共享相同的文件ID。
- 文件写入令牌 - 每次尝试写入基本文件时单调递增的令牌。这有助于在出现故障和重试时唯一地标识基本文件。清理可以删除部分/未提交的基本文件,前提是写入令牌不是文件组中的最新令牌。
- 提交时间戳 - 与创建此基本文件的时间线上的提交时刻匹配的时间戳
- 文件扩展名 - 表示开源文件格式的基本文件扩展名,如.parquet
属于同一文件组的所有文件切片(Parquet文件)将共享相同的**[文件ID]**值。然后,FileGroup的当前(最新)版本是**[提交时间戳]**值最高的文件切片。
示例:
Hudi采用了多版本并发控制(MVCC),其中清理操作可以摆脱未使用/旧的文件切片。
CoW表文件管理
这里我们可以看到Hudi如何随时间管理COW表的底层文件。

- 在时间T1,我们在分区1中写入100MB的数据:
- 创建了一个由文件ID XX标识的FileGroup,并在其中创建了一个FileSlice
- 在此文件切片中,我们有一个名为XX-t1.parquet的Parquet文件,其中包含100MB的数据
- 在时间T2,我们在分区2中写入90MB的数据:
- 创建了一个由文件ID ZZ标识的FileGroup,并在其中创建了一个FileSlice
- 在此文件切片中,我们有一个名为ZZ-t2.parquet的Parquet文件,其中包含90MB的数据
- 在时间T3,我们在分区1中更新1MB的数据:
- 为FileGroup XX创建了一个新的文件切片
- 在此文件切片中,我们有一个新的Parquet文件,其中包含前一个FileGroup切片的所有数据以及更新
- 在时间T4,我们在分区1中插入50MB的数据:
- 为FileGroup XX创建了一个新的文件切片
- 在此文件切片中,我们有一个新的Parquet文件,其中包含前一个FileGroup切片的所有数据以及部分插入数据,即20MB,因为这达到了最大文件大小
- 对于剩余的30MB:
- 创建了一个名为YY的新文件组
- 为FileGroup YY创建了一个新的文件切片
- 在此文件切片中,我们有一个新的Parquet文件,其中包含剩余的30MB插入数据
- 在时间T5,我们在分区1中插入20MB的数据:
- 为FileGroup YY创建了一个新的文件切片
- 在此文件切片中,我们有一个新的Parquet文件,其中包含前一个FileGroup切片的所有数据以及插入数据
Hudi CoW表时间线元数据
Hudi将对数据集执行的所有活动的元数据维护为时间线,这使得数据集的即时视图成为可能。Hudi库有效地使用.hoodie子文件夹内部管理一个表,以跟踪所有元数据。
时间线包含有关特定Hudi操作的类型、时间戳和状态的信息。
这里我们可以看到与上述示例相关的Hudi时间线元数据。

- 在T1时刻,插入操作完成后,我们有一个COMMIT操作。在提交操作开始时,创建了一个名为/.hoodie/<t1>.commit.requested的文件。然后创建了一个名为/.hoodie/<t1>.inflight的文件。最后,当提交操作完成时,创建了/.hoodie/<t1>.commit文件。
- 对于T2、T3、T4和T5的其他4个操作也发生了同样的事情。
.commit文件是一个JSON文件,包含以下信息:
- 对于每个分区的一些写入统计信息:
- 对于每个文件组:
- 新文件切片的名称
- 新文件切片的大小
- 写入的记录数
- 删除的记录数
- 更新的记录数
- 插入的记录数
- 文件组中前一个文件切片的提交时间
- 当前模式
- 操作类型(即插入、更新等)