Apache Hudi 1.1 正式发布,这一重要版本为下一代数据湖仓能力奠定了坚实基础。本次发布聚焦于底层架构的增强、引擎专属优化以及关键性能改进,为未来版本中更宏大的功能铺平了道路。
可插拔表格式框架——多格式支持的基石
Hudi 1.1 引入了可插拔表格式框架,将 Hudi 强大的存储引擎能力开放给 Apache Iceberg、Delta Lake 等其他表格式。这代表了 Hudi 在表格式支持方式上的根本性转变,实现了对多种格式的原生集成,为您提供了一个具备跨格式完全读写兼容性的统一系统。
愿景与设计
在现代湖仓生态中,表格式的选择多样且持续演进。Hudi、Iceberg、Delta Lake 等不同格式各有千秋。Hudi 1.1 没有采用“一刀切”的方案,而是通过可插拔表格式框架拥抱开放的湖仓生态,避免供应商锁定。
该框架建立在一个清晰的抽象层之上,将 Hudi 的核心能力——事务管理、索引、并发控制和表服务——与用于数据文件的具体存储格式解耦。设计的核心是 HoodieTableFormat 接口,不同的格式实现可以扩展此接口。
关键架构组件
- 存储引擎:Hudi 的存储引擎能力(如时间线管理、并发控制机制、索引和表服务)可以跨多种表格式工作。
- 可插拔适配器:特定格式的实现负责在写入时生成符合规范的元数据。
Hudi 自身的构件提供对原生 Hudi 格式的支持,而 Apache XTable(孵化中)则提供可插拔的格式适配器。这一架构使组织能够为每个用例选择最合适的格式,同时保持统一的操作体验,并在所有格式上利用 Hudi 成熟的存储引擎。
在 1.1 版本中,该框架默认通过 hoodie.table.format=native 配置提供对原生 Hudi 格式的支持,现有用户无需任何更改。真正的亮点在于未来:该框架为支持 Iceberg 和 Delta Lake 等附加格式铺平了道路。想象一下,您可以利用 Hudi 的记录级索引能力高效地向 Hudi 表写入高频更新,同时通过支持广泛读取目录的 Iceberg 适配器维护 Iceberg 元数据。1.1 中的可插拔表格式框架使此类使用模式成为可能。
索引改进——更快更智能的查找
Hudi 的索引子系统是其最强大的功能之一,能够在写入时实现快速记录查找,在读取时实现高效的数据跳过。
分区记录索引
自 0.14.0 版本起,Hudi 在索引子系统中支持全局记录索引。然而,许多场景(例如按日期分区的用户事件)仅要求记录键在分区内唯一。Hudi 1.1 引入了分区记录索引,这是记录索引的非全局变体,它结合分区路径和记录键进行工作,利用分区信息在查找时修剪无关分区,显著减少搜索空间,从而即使在海量数据集上也能实现高效查找。
-- Spark SQL:创建启用分区记录索引的表
CREATE TABLE user_activity (
user_id STRING,
activity_type STRING,
timestamp BIGINT,
event_date DATE
) USING hudi
TBLPROPERTIES (
'primaryKey' = 'user_id',
'preCombineField' = 'timestamp',
-- 启用分区记录索引
'hoodie.metadata.record.level.index.enable' = 'true',
'hoodie.index.type' = 'RECORD_LEVEL_INDEX'
)
PARTITIONED BY (event_date);
分区记录索引使得索引查找的性能与分区大小成比例扩展,优化了异构数据分布下的性能。
分区级桶索引
桶索引因其通过确定性映射将键映射到文件组从而消除昂贵的记录查找,而成为高吞吐量写入工作负载的热门选择。但现有桶索引有个关键限制:一旦设置了桶数量,更改它需要重写整个表。
1.1 版本引入了分区级桶索引,允许通过基于正则表达式的规则为不同分区设置不同的桶数量。这使得表能够随着数据量的变化而适应——例如,较旧的、较小的分区可以使用较少的桶,而较新的、较大的分区可以拥有更多的桶。
-- Spark SQL:创建启用分区级桶索引的表
CREATE TABLE sales_transactions (
transaction_id BIGINT,
user_id BIGINT,
amount DOUBLE,
transaction_date DATE
) USING hudi
TBLPROPERTIES (
'primaryKey' = 'transaction_id',
-- 分区级桶索引
'hoodie.index.type' = 'BUCKET',
'hoodie.bucket.index.hash.field' = 'transaction_id',
'hoodie.bucket.index.partition.rule.type' = 'regex',
'hoodie.bucket.index.partition.expressions' = '2023-.*,16;2024-.*,32;2025-.*,64',
'hoodie.bucket.index.num.buckets' = '8'
)
PARTITIONED BY (transaction_date);
分区级桶索引非常适合分区大小随时间显著变化的时间序列数据。更多信息请参阅文档和 RFC 89。
索引性能优化
除了新索引,Hudi 1.1 还为元数据表操作带来了显著的性能提升:
- HFile 块缓存与预取:新的块缓存将最近访问的数据块存储在内存中,避免从存储重复读取。对于较小的 HFile,Hudi 会预先获取整个文件。基准测试显示,重复查找速度提升了约 4 倍,此功能默认启用。
- HFile 布隆过滤器:为 HFile 添加布隆过滤器使得 Hudi 可以在获取数据块之前快速判断某个键是否可能存在于文件中,避免了不必要的 I/O,极大加快了点查速度。您可以通过
hoodie.metadata.bloom.filter.enable=true 启用。
这些优化叠加使得元数据表显著加快,直接提升了所有 Hudi 表的写入和读取性能。此外,Hudi 1.1 添加了其原生的 HFile 写入器实现,消除了对 HBase 库的依赖,显著减少了 Hudi 包的大小。
通过 Parquet 文件二进制拷贝加速聚类
聚类操作可以重组数据以提升查询性能,但传统方法成本高昂——即使不需要转换,也需要对数据进行解压、解码、转换、重新编码和重新压缩。
Hudi 1.1 为聚类操作实现了 Parquet 文件二进制拷贝。当模式兼容时,此优化会直接将 Parquet 行组从源文件拷贝到目标文件,完全消除了冗余的转换过程。
在 100GB 的测试数据上,与常规重写 Parquet 文件相比,使用二进制拷贝实现了 15 倍的执行速度提升(18 分钟 → 1.2 分钟)和 95% 的计算资源减少(28.7 任务小时 → 1.3 任务小时)。在真实场景下对 1.7TB 数据集(300 列)的验证显示,性能提升了约 5 倍(35 分钟 → 7.7 分钟),CPU 使用率从 90% 降至 60%。
该优化目前支持写时复制表,并在安全时自动启用;当需要进行模式协调时,Hudi 会智能地回退到传统聚类方式。
基于存储的锁提供者——消除并发写入的外部依赖
对于生产数据湖仓,多写入器并发至关重要,多个作业需要同时写入同一张表。过去,在 Hudi 中启用多写入器支持需要设置如 AWS DynamoDB、Apache Zookeeper 或 Hive Metastore 等外部锁提供者。这些方案虽然有效,但增加了运维复杂性。
Hudi 1.1 引入了基于存储的锁提供者,它通过直接使用表存储层中的 .hoodie/ 目录来管理并发,完全消除了此依赖。
该实现在 .hoodie/.locks/ 下的单个锁文件上使用条件写入,以确保同一时间只有一个写入器持有锁,并具备基于心跳的续租和自动过期机制以实现容错。要使用基于存储的锁提供者,您需要添加对应的 Hudi 云捆绑包(S3 用 hudi-aws-bundle,GCS 用 hudi-gcp-bundle)并设置以下配置:
hoodie.write.lock.provider=org.apache.hudi.client.transaction.lock.StorageBasedLockProvider
这种方法消除了对 DynamoDB、ZooKeeper 或 Hive Metastore 的依赖,降低了运维成本和基础设施复杂度。这种云原生设计直接与 S3 或 GCS 存储特性协同工作,计划支持更多存储系统,使 Hudi 在云原生环境中更易于大规模运维。
使用合并模式与自定义合并器——告别 Payload 类
Hudi 的一个核心设计原则是让存储层理解如何合并对同一记录键的更新,即使这些变更到达顺序不一致。在 Hudi 1.1 之前,记录合并逻辑主要通过 Payload 类实现,这些类较为分散且缺乏标准化语义。
Hudi 1.1 弃用了 Payload 类,并鼓励用户采用自 1.0 以来引入的新 API 进行记录合并:合并模式和 HoodieRecordMerger 接口。
合并模式——声明式记录合并
对于常见用例,COMMIT_TIME_ORDERING 和 EVENT_TIME_ORDERING 合并模式提供了一种声明式的方式来指定合并行为:
- COMMIT_TIME_ORDERING:选择具有最高完成时间/时刻的记录作为最终合并结果(标准关系语义或到达时间处理)。
- EVENT_TIME_ORDERING:选择在用户指定的排序字段上具有最高值的记录作为最终结果。支持事件时间处理语义,以处理迟到数据而不破坏记录状态。
默认行为是自适应的:如果未配置排序字段 (hoodie.table.ordering.fields),Hudi 默认使用 COMMIT_TIME_ORDERING;如果设置了一个或多个排序字段,则使用 EVENT_TIME_ORDERING。
自定义合并器——灵活的方式
对于复杂的合并逻辑(例如字段级协调、聚合计数器或保留审计字段),HoodieRecordMerger 接口提供了 Payload 类之外的现代化、引擎原生的替代方案。您需要将合并模式设置为 CUSTOM 并提供自己的 HoodieRecordMerger 实现。通过使用新 API,您可以在所有代码路径(预合并、更新写入、压缩和快照读取)上实现一致的合并。
Apache Spark 集成改进
Spark 仍然是使用 Hudi 表最受欢迎的引擎之一,1.1 版本带来了几项重要增强。
Spark 4.0 支持
Spark 4.0 为 ML/AI 工作负载带来了显著的性能提升,通过自动连接策略切换实现更智能的查询优化,具备动态分区倾斜缓解和增强的流处理能力。Hudi 1.1 添加了对 Spark 4.0 的支持。要开始使用,请在您的依赖列表中使用新的 hudi-spark4.0-bundle_2.13:1.1.0 构件。
元数据表流式写入
Hudi 1.1 引入了对元数据表的流式写入,将数据和元数据写入统一到单个 RDD 执行链中。关键设计是在数据写入期间直接在各个执行器上并行生成元数据记录,消除了先前造成瓶颈的冗余文件查找,并增强了在 Spark 中执行阶段重试时的可靠性。
针对更新密集型工作负载的基准测试显示,与 Hudi 1.0 相比,此功能为启用记录索引的表带来了约 18% 的更快速写入时间。该功能对 Spark 写入器默认启用。
新增与增强的 SQL 过程
Hudi 1.1 扩展了 SQL 过程库,新增了用于表管理和可观测性的实用功能,将运维能力直接带入 Spark SQL。
新增的过程 show_cleans、show_clean_plans 和 show_cleans_metadata 提供了对清理操作的可见性:
CALL show_cleans(table => 'hudi_table', limit => 10);
增强的 run_clustering 过程支持使用正则表达式模式进行分区过滤:
-- 聚类所有匹配模式的2025年分区
CALL run_clustering(
table => 'hudi_table',
partition_regex_pattern => '2025-.*',
);
所有 show 过程(在适用情况下)都增强了 path 和 filter 参数。例如,这对于复杂的后端排查非常有用:
-- 查找近期分区中的大文件
CALL show_file_status(
path => '/data/warehouse/transactions',
filter => "partition LIKE '2025-11%' AND file_size > 524288000"
);
新增和增强的 SQL 过程将表管理直接带入 Spark SQL,简化了以 SQL 为中心的工作流的操作。
Apache Flink 集成改进
Flink 是实时数据管道的热门选择,Hudi 1.1 为 Flink 集成带来了实质性改进。作为优秀的实时计算引擎,其与 Hudi 的深度整合为构建实时数据湖仓提供了强大动力,更多关于大数据技术的实践可参考社区内容。
Flink 2.0 支持
Hudi 1.1 带来了对 Flink 2.0 的支持。Flink 2.0 引入了分离式状态存储(ForSt),将状态与计算解耦以实现无限扩展;异步状态执行以提高资源利用率;自适应广播连接以实现高效查询处理;以及物化表以简化流批一体。使用新的 hudi-flink2.0-bundle:1.1.0 构件开始体验。
引擎原生记录支持
Hudi 1.1 通过直接处理 Flink 的原生 RowData 格式,消除了昂贵的 Avro 转换,在整个管道中实现了零拷贝操作。这一自动变更(无需配置)相比 Hudi 1.0 平均带来了 2-3 倍的写入和读取性能提升。
一项基准测试插入了 5 亿条记录(模式为 1 个 STRING 和 10 个 BIGINT 字段):Hudi 1.1 实现了每秒 235.3k 条记录,而 Hudi 1.0 为每秒 67k 条记录——吞吐量提升了 3 倍以上。
缓冲排序
对于仅追加表,Hudi 1.1 引入了内存缓冲排序,在将记录刷新到 Parquet 之前对其进行预排序。这通过更好的最小/最大过滤带来了 15-30% 的更好压缩和更快的查询。您可以通过 write.buffer.sort.enabled=true 启用此功能,并通过 write.buffer.sort.keys 指定排序键(例如 "timestamp,event_type")。您也可以通过 write.buffer.size(默认 1000 条记录)调整用于排序的缓冲区大小。
新集成:Apache Polaris(孵化中)
Polaris(孵化中)是一个用于湖仓平台的开源目录,它提供多引擎互操作性和跨不同表格式与查询引擎的统一治理。其关键特性是使数据团队能够在数据的单一副本上使用多个引擎(Spark、Trino、Dremio、Flink、Presto),并具有一致的元数据。
Hudi 1.1 引入了与 Polaris 的原生集成(等待包含此 PR 的 Polaris 发布),允许用户在 Polaris 目录中注册 Hudi 表,并从任何 Polaris 兼容的引擎查询它们,从而简化多引擎工作流。
展望未来
Hudi 的未来令人兴奋。基于 1.1 的坚实基础,社区正在积极开发面向 AI/ML 的变革性能力,包括用于高效存储嵌入和文档的非结构化数据类型和列组、Lance、Vortex、Blob 优化的 Parquet 支持以及湖仓表的向量搜索能力。这仅仅是个开始,我们正在重新构想湖仓的可能性,从多格式互操作性到下一代 AI/ML 工作负载。