跳到正文

在 Uber 运行高性价比的导出工作负载

了解 Uber 如何利用 #ApacheHudi 列统计信息和表排序来优化 #GCS 存储成本和出口流量,并提升选择性 #DSAR 和隐私合规导出工作负载的延迟表现。#Engineering #DataLakehouse #CloudOptimization #ApacheSpark #Parquet

本文目录文章开头

译文尚不完整,未译段落保留原文;可打开原文对照。

Introduction

At Uber, several offline export workloads retrieve small record sets from large historical datasets. Today, these selective queries can still trigger full-table scans. On a Google Cloud Storage™-backed lakehouse like that at Uber, repeated reads keep large table regions hot, increasing storage, retrieval, metadata-operation, GCS egress, and latency costs. This blog shows how Apache Hudi™ column stats in conjunction with sorting/clustering can reduce scan volume and make these workloads more cost-efficient in that lakehouse environment.

What Are Export Workloads?

Export workloads are common in compliance, privacy, and operational export flows, where the result set is small but the search space spans the entire history of data.

Export workloads typically have the following characteristics:

  • They issue point queries or narrow predicate queries over large transaction or fact tables stored in Hudi and partitioned by date
  • They run repeatedly, often multiple times per week.
  • They repeat the same query pattern, but with different parameters each time.
  • The target record set is only a small fraction of the overall dataset
  • Despite their selectivity, they perform full-table scans to find the relevant records

One concrete example of this workload pattern is a DSAR request. A DSAR (Data Subject Access Request) is a legal request by an individual to access the personal data an organization holds about them. In practice, fulfilling a DSAR often translates into a narrow query pattern like the one shown in Figure 1.

SQL query selects trip_id, from_city, to_city from trip_table where trip_date is on or before today.

Figure 1: Narrow query pattern.

GCS Auto-Class Tiering

Uber uses GCS (Google Cloud Storage) as the storage layer for its lakehouse. GCS offers multiple storage classes that trade off storage cost, retrieval cost, and access latency. Understanding these tiers is important because the cost efficiency of export workloads depends not only on how much data is stored, but also on how often that data is touched. 

Comparison of Standard, Nearline/Coldline, and Archive storage for data access frequency, cost, and retention.

Figure 2: Storage tier distinctions in GCS.

GCS also provides auto-class tiering, which automatically moves objects between storage classes based on observed access patterns. In principle, this allows colder data to move into cheaper storage tiers over time. However, this mechanism is undermined by workloads that repeatedly touch all the files preventing the autoclass tiering feature to kick-in. Repeatedly scanning file metadata or footers can also keep objects hot and prevent them from transitioning into lower-cost storage classes. A desired  distribution based on typical access patterns  is approximately 60% Standard, 15% Nearline, 10% Coldline, and 15% Archive. When repeated reads on all the files keep colder data hot, 100 percent of data ends up in Standard tier and stays there. The result is you pay the highest storage cost.

Export Workloads Break Auto-Class Tiering

Export workloads disrupt auto-class tiering because they combine high selectivity with a large historical search space. A query may return only a few records while still touching all the partitions and files.

分析工作负载通常会处理更多数据,但集中在较新的分区上,让较旧的数据变冷。导出工作负载则相反:它们会反复在历史数据中搜索一个很小的目标集合。这种反复在大海捞针的模式中——大海就是整张表——会保持数据处于热状态,阻止自动分类转换,并增加存储和检索成本。

再次考虑 trip_table 这个例子。

图 3 展示了一个典型分析查询的样子。

SQL query counts trips between cities in the last two days, grouped by departure and destination cities.

图 3:一个典型的分析查询。

该查询在聚合的数据方面很广泛,但在时间范围上很窄,因为它聚焦于最近的分区。

相比之下,图 4 展示了导出查询的样子。

SQL query selects trip_id, from_city, to_city for a specific user_id and trips up to today from trip_table.

图 4:一个典型的导出查询。

该查询在它要查找的记录方面很窄,但在它可能需要扫描的历史范围上很广。即使结果集很小,引擎可能仍需检查许多分区中的文件,以确定相关记录存在于何处。

其结果是,存储访问足迹远大于仅从输出大小所推断的规模。反复执行这种模式会增加存储成本、检索成本,以及元数据密集的 Class B 操作(如 objects.get 和 objects.list)。它还可能通过强制对本应保持冷状态的文件进行反复扫描而降低查询性能。

对 Hudi 表中扫描范围的可视化使这种对比变得清晰。

Analytics workload accesses recent parquet files; DSAR workload accesses all files across all partitions.

图 5:对比典型分析与导出工作负载文件扫描模式的示意图。

全表扫描模式进一步推高了计算和 GCS 出网成本。对于大规模数据集,这种执行模式会触发显著的资源开销,并有超出峰值 带宽限制 的风险。

二级索引无济于事

为实现我们的优化和效率目标,我们专注于最小化查询执行期间接触的数据文件数量。我们评估了 Hudi 二级索引 作为潜在解决方案。Hudi 中的二级索引旨在通过帮助查询引擎在扫描数据之前进行文件裁剪,来加速非主键列上的谓词。Hudi 通过其元数据表索引框架支持这些索引,并且当表具备所需的 record-index 设置时,可以使用 SQL 创建二级索引。

对于我们的导出工作负载,关键问题不在于二级索引能否裁剪文件,而在于它能否裁剪足够多的文件,以证明其额外的元数据和运维成本是合理的。在我们的目标表上,同一主体的记录分散在大多数文件中。二级索引有助于定位候选行,但它仍可能映射到大量文件。因此,引擎仍需承担较高的文件扫描和元数据成本。

什么方法有效?

为实现减少文件扫描数量的目标,我们结合了两个思路:

  • Hudi 列统计信息,用于在不接触底层 Parquet 数据文件的情况下确定哪些文件与某个谓词相关
  • 在谓词列上对表进行排序,使匹配记录聚集到更小的文件子集中,从而显著提高文件裁剪效果,因为查询保持不变,并且始终过滤一致的谓词。

HUDI architecture diagram showing metadata and data tables, ingestion, query engine, and sorting jobs interactions.

图 6:架构图。

Hudi 列统计信息

Hudi 在其元数据表中维护 列级统计信息,包括被跟踪列的最小值、最大值、空值计数和总计数等值。由于此元数据与底层 Parquet 数据文件分开存储,查询引擎可以使用它来裁剪无关文件,而无需直接读取这些文件。

对于反复对大型历史数据集运行选择性谓词的导出工作负载,这避免了触及表的大部分内容,并减少了扫描范围。

Parquet 存储了有用的列统计信息,但它们位于文件页脚中。在我们的环境中,仅依赖这些页脚是不够的,因为读取它们仍然会触及数据文件。随着时间的推移,这些重复读取会使文件保持“热”状态,从而阻止 auto-class 将它们移动到更冷、更便宜的存储层。

Hudi 通过在表级别维护文件裁剪元数据来解决页脚读取问题,而不是要求引擎单独检查每个 Parquet 文件。引擎无需打开许多文件来发现其统计信息,而是可以查询 Hudi 的元数据表,并在开始扫描数据之前确定哪些文件是相关的。

这使得文件裁剪对于导出工作负载来说既更快又更节省存储。它减少了触及冷文件的需求,保留了 auto-class 分层的好处,并降低了重复历史查询的运营成本。

表排序

导出工作负载查询通常由主体标识符驱动,例如骑手 ID、司机 ID 或快递员 ID。如果某个主体的记录分散在许多文件中,即使有列统计信息,引擎可能仍需检查表的大部分内容。

在谓词列上排序会将相似的键聚集在一起,收紧文件级别的最小/最大值范围,并使裁剪更具选择性。列统计信息提供裁剪机制;排序则提高其有效性。两者结合,将广泛的文件扫描转变为精确的文件选择,从而减少行级查询的读取、shuffle 和执行时间。

基准测试

我们对一个真实表分区进行了排序,并取 100、200、500、1,000 和 5,000 个谓词(用户 ID),对分区进行排序,比较目标行分散在其中的 Parquet 文件数量。

Bar chart comparing target file percentages for unsorted and sorted data across different user counts, showing unsorted dominance.

图 7:在非排序分区与排序分区中,不同谓词数量下的目标文件百分比。

我们进行的下一个基准测试是,将一个分区按谓词列作为排序键进行排序,并记录在文件裁剪(扫描期间从未触及)、GCS 出站流量和扫描期间计算量减少方面的效率提升。

Table shows disk reduction of 24.8%, with files pruned and reductions for low, mid, and high predicate values.

图 8:文件裁剪、出站流量、计算量减少和存储减少的基准测试结果。

该方案减少了每次导出查询所涉及的分区和文件数量。对于导出工作负载,主要的低效之处不在于输出大小,而在于引擎必须检查的扫描范围。最大限度地减少文件扫描,可让较旧的数据在 GCS 自动分类中保持冷状态,并减少重复的数据访问、元数据操作、GCS 出站流量和查询开销。此外,表排序通过提高压缩效率来优化存储,因为将相同的值在物理上聚集在一起会使总数据占用空间更小。

为什么不采用排序配合二级索引?

排序使剪枝更加有效。而此时,Hudi 列统计信息已经能够提供有效的文件剪枝,且其元数据占用空间比二级索引更小。列统计信息的规模主要取决于文件数量和被跟踪的列数,而二级索引必须为被索引的值维护查找映射。

在我们的工作负载中,排序提供物理聚簇,而列统计信息索引提供轻量级的剪枝机制,相比之下二级索引要沉重得多。

对比矩阵

Comparison table of Sorted + Hudi Secondary Index vs. Sorted + Hudi Column stats for index size and compute overhead.

图 8:排序表中的目标文件。

对于我们的导出工作负载,这使得权衡变得清晰:如果表未排序,二级索引无法剪枝掉足够多的文件来实质性地改变扫描成本;如果表已排序,Hudi 列统计信息提供了一种更简单且更节省存储的解决方案。因此,我们选择了排序表加 Hudi 列统计信息,而非基于二级索引的设计。

在其他行业用例中的适用性

这种底层的导出访问模式出现在许多领域中,在这些领域里,系统需要从非常庞大的历史数据集中检索少量记录,且通常受到严格的合规性、运营或调查要求的约束。常见的模式是在较长的保留期内进行面向特定主体的窄范围查找。

示例包括:

  • 金融:监管、审计和争议处理工作流通常需要特定账户或实体的多年历史记录
  • 医疗保健和生命科学:合规性审查、患者历史访问和审计工作流通常需要对长期保留的记录进行选择性查找
  • 电子商务和零售:欺诈调查、客户支持工作流和订单历史导出经常需要跨长时间范围检索少量用户的记录
  • 广告技术和衡量:用于归因、个性化或合规的时间线重建通常需要跨历史事件数据进行主体级别的查找
  • 安全和身份系统:调查、访问审查和合规工作流通常需要特定用户或主体的完整登录或活动历史记录

后续步骤

我们正专注于将这些优化扩展到 Uber 更广泛的基础设施中。

我们的首要目标是解决多列查找场景。对于各种客户端使用不同谓词列的数据集,单列排序往往无法提供统一的收益。为了克服随着列数增加标准字典序排序的局限性,我们正在探索 Z-order 空间填充曲线,以实现更有效的数据布局优化。

此外,我们计划开发一个自动化框架来检测这类大海捞针式的访问模式。该系统将权衡索引和排序的成本与潜在的查询节省,仅在投资回报率合理时才触发数据布局优化管道。

结论

以下是我们从这项工作中吸取的一些经验教训: 

  • 访问模式决定存储成本。跨越历史的窄范围查找与典型的分析查询表现截然不同,而简单的表布局可能会造成不成比例的存储和出口成本。
  • 文件布局与分层同样重要。 GCS 自动分类只有在文件长时间未被访问而变冷时才能带来节省。排序加上 Hudi 元数据有助于最大限度地减少不必要的文件访问。
  • 基于元数据的剪枝至关重要。 依赖 Parquet 页脚与分层相悖,因为读取页脚仍然会触及文件。Hudi 列统计信息可在不反复访问数据文件的情况下实现剪枝。
  • 一次性重写可以带来长期节省。 对表进行排序和重写会带来前期成本,但它减少了扫描范围,并能随着时间的推移实现持续的存储节省。
  • 最佳结果来自端到端设计。 存储布局、元数据、查询执行和编排都需要协同工作;否则,成本只是从一个层转移到另一个层。
  • Hudi Table Service 是主要的促成因素。 Hudi Table Service 在促成这些基于访问模式的优化方面发挥着关键作用,它是管理和执行所有回填排序、索引构建和增量排序操作所需的核心引擎。

总体而言,Uber 优化导出工作负载的实践表明,云存储成本在很大程度上取决于访问模式和文件布局。通过将 Hudi 列统计信息与谓词列排序相结合,我们将广泛的历史扫描转变为选择性强、低触碰的查找,从而持续节省存储和出口成本。

致谢 

Meenal Binwade Vamshi Pasunuru Jiashen Zhang Uber 的 Hudi 团队在将列统计信息投入生产方面发挥了重要作用。

封面图片出处:由 Gemini 生成

Apache®、Apache Hudi™ 和星形标志是 Apache Software Foundation 在美国和/或其他国家的注册商标或商标。使用这些标志并不意味着 Apache Software Foundation 的认可。

Google Cloud Storage™ 是 Google LLC 的商标,本博文未以任何方式得到 Google 的认可或与 Google 有关联。

阅读显示设置

调整正文排版

当前浏览器记住选择,应用于文章正文。手机上正文宽度以屏幕可用空间为限。

图表完整性提示

普通文章遇到图表保存问题时显示提示。