Hudi 数据模型分析

01. Hudi 数据模型分析

主题说明

Hudi 的数据模型是整个系统的核心抽象,说白了就是定义了数据记录在系统中是怎么表示的、怎么操作的。理解数据模型是理解 Hudi 工作原理的基础,就像盖房子要先打地基一样。

在 Hudi 里,一条数据记录不是简单的字符串或者字节数组,而是一个结构化的对象,包含了记录本身的数据、唯一标识、存储位置等信息。这种设计让 Hudi 能够高效地管理数据,支持更新、删除、合并等复杂操作。

细化内容

HoodieRecord - 记录的基础抽象

HoodieRecord 是所有记录的抽象基类,它定义了记录的基本结构。简单来说,一条 HoodieRecord 包含:

  • key :记录的键,类型是 HoodieKey,用来唯一标识这条记录
  • data :记录的实际数据,类型是泛型 T,通常是 HoodieRecordPayload 的实现
  • currentLocation:记录当前在存储中的位置,包含文件ID和时间戳
  • newLocation:记录写入后的新位置
  • operation:操作类型,比如 INSERT、UPDATE、DELETE

这个设计很巧妙,把记录的标识、数据、位置信息都封装在一起了。这样在更新数据的时候,可以快速定位到记录在哪里,然后进行合并操作。

HoodieKey - 记录的唯一标识

HoodieKey 是记录的唯一标识,包含两个字段:

  • recordKey:记录的主键,比如用户ID、订单号等
  • partitionPath :分区路径,比如 2023/01/01 这样的日期分区

这两个字段组合起来就能唯一确定一条记录。比如用户ID是 user123,分区是 2023/01/01,那么这条记录在表中的位置就确定了。

HoodieRecordPayload - 数据负载的抽象

HoodieRecordPayload 是一个接口,定义了数据合并的逻辑。这是 Hudi 最核心的部分之一,因为它决定了当同一条记录有多个版本时,应该怎么合并。

主要方法包括:

  • preCombine:在写入前合并同一批次中的重复记录
  • combineAndGetUpdateValue:合并存储中的旧记录和新的更新记录
  • getInsertValue:获取要插入的新记录

不同的实现类有不同的合并策略:

  • OverwriteWithLatestAvroPayload:直接用新值覆盖旧值
  • DefaultHoodieRecordPayload:根据排序字段选择最新的值
  • EventTimeAvroPayload:基于事件时间合并

HoodieAvroRecord - 基于 Avro 的记录实现

HoodieAvroRecordHoodieRecord 的具体实现,使用 Avro 格式来存储数据。

什么是 Avro?

Avro 是 Apache 的一个数据序列化系统,简单说就是把数据转换成二进制格式,方便存储和传输。Avro 有几个特点:

  1. Schema 驱动:数据结构和 Schema 是分开的,Schema 可以独立存储
  2. 紧凑的二进制格式:比 JSON 等文本格式更省空间
  3. 支持 Schema 演化:可以修改 Schema 而不影响已有数据
  4. 跨语言支持:Java、Python、C++ 等都能用

在 Hudi 里,Avro 主要用于存储增量日志(LogFile),因为它是行式存储,写入性能好。而基础文件(BaseFile)用的是 Parquet,列式存储,查询性能好。

元数据字段

每条 Hudi 记录都包含一些元数据字段,这些字段是系统自动添加的:

  • _hoodie_commit_time:提交时间,记录这条数据是什么时候写入的
  • _hoodie_commit_seqno:提交序列号,同一个提交内的记录排序
  • _hoodie_record_key:记录键
  • _hoodie_partition_path:分区路径
  • _hoodie_file_name:文件名,记录这条数据在哪个文件里
  • _hoodie_operation:操作类型(INSERT、UPDATE、DELETE)

这些元数据字段让 Hudi 能够追踪每条记录的历史,支持时间旅行查询等功能。

关键技术

记录的序列化和反序列化

Hudi 使用 Kryo 来序列化记录,Kryo 是一个高效的 Java 序列化框架。序列化就是把对象转换成字节数组,方便在网络传输或者持久化存储。

HoodieRecord 中,实现了 KryoSerializable 接口,可以自定义序列化逻辑。这对于大数据场景很重要,因为序列化的性能直接影响整体性能。

记录的合并策略

Hudi 支持多种合并策略,主要通过 HoodieRecordPayload 的不同实现来支持:

  1. OverwriteWithLatest:直接用新值覆盖,最简单粗暴
  2. EventTimeBased:基于事件时间,选择时间最新的记录
  3. PartialUpdate:部分更新,只更新指定字段
  4. Custom:自定义合并逻辑

合并策略的选择取决于业务需求。比如订单表,通常用 OverwriteWithLatest;而用户行为表,可能用 EventTimeBased。

记录的排序和分区

记录在写入前会进行排序,排序的依据可以是:

  • 提交时间(默认)
  • 自定义排序字段
  • 分区路径

排序的目的是优化写入性能,把相同分区的记录放在一起写入,减少文件数量。

记录的元数据管理

元数据字段是自动管理的,不需要用户手动设置。系统会在写入时自动填充这些字段,在读取时自动解析。

关键对象说明

类关系图

关键类说明

  • HoodieRecord:抽象记录类,定义了记录的基本结构和方法。它是所有记录类型的基类。
  • HoodieKey:记录键,包含 recordKey 和 partitionPath。实现了 equals 和 hashCode,用于记录的去重和查找。
  • HoodieRecordPayload:记录负载接口,定义了数据合并的核心逻辑。不同的实现类有不同的合并策略。
  • HoodieAvroRecord:基于 Avro 的记录实现,是 Hudi 中最常用的记录类型。它把数据序列化成 Avro 格式存储。
  • HoodieRecordLocation:记录位置,包含 fileId(文件ID)和 instantTime(时间戳)。用于定位记录在存储中的位置。

关键操作时序图

下面是一个记录合并操作的时序图,展示了当更新一条已存在的记录时,各个类是如何协作的:

这个时序图展示了:

  1. 客户端调用 upsert 方法
  2. 表通过索引查找记录位置
  3. 合并器合并新旧记录
  4. Payload 执行具体的合并逻辑
  5. 写入合并后的记录

代码示例

创建一条记录

java 复制代码
// 创建记录键
HoodieKey key = new HoodieKey("user123", "2023/01/01");

// 创建 Avro 记录数据
GenericRecord avroRecord = new GenericData.Record(schema);
avroRecord.put("id", "user123");
avroRecord.put("name", "张三");
avroRecord.put("age", 25);

// 创建 Payload
HoodieAvroPayload payload = new HoodieAvroPayload(
    Option.of(avroRecord), 
    System.currentTimeMillis() // 排序值
);

// 创建 Hudi 记录
HoodieRecord<HoodieAvroPayload> record = 
    new HoodieAvroRecord<>(key, payload);

记录合并示例

java 复制代码
// 假设存储中有一条旧记录
IndexedRecord oldRecord = ...; // 从存储读取

// 新来的更新记录
HoodieAvroPayload newPayload = new HoodieAvroPayload(newRecord, timestamp);

// 执行合并
Option<IndexedRecord> merged = newPayload.combineAndGetUpdateValue(
    oldRecord, 
    schema, 
    properties
);

// merged 就是合并后的结果
if (merged.isPresent()) {
    // 写入合并后的记录
    writeRecord(merged.get());
} else {
    // 返回空表示删除这条记录
    deleteRecord(key);
}

总结

Hudi 的数据模型设计得很巧妙,把记录的标识、数据、位置信息都封装在一起。核心要点:

  1. HoodieRecord 是基础抽象,包含 key、data、location 等核心属性
  2. HoodieKey 是唯一标识,由 recordKey 和 partitionPath 组成
  3. HoodieRecordPayload 定义了合并逻辑,支持多种合并策略
  4. Avro 是行式存储格式,适合增量日志,写入性能好
  5. 元数据字段 自动管理,支持时间旅行等高级功能

理解数据模型是理解 Hudi 的基础,后续的存储、索引、查询等功能都建立在这个模型之上。