Introduction: The evolution of Grab’s Data Lake

在 Grab 的规模下,管理跨越数十亿 S3 对象的 PB 级数据需要的不仅仅是一个存储层。它需要一个健壮的架构原语,以支持现代 “Lakehouse” 的高并发需求。我们的目标是实现完整的存储-计算分离,利用 S3 作为弹性基础,支持近实时指标和大批量转换。

多年来,我们绝大多数表都是通过 Hive Metastore 管理的目录式布局的 Hive Parquet。这种模式曾很好地服务了我们,但随着数据量增长,基于目录和元存储的方法逐渐成为瓶颈。我们正在转向以表为中心的架构,基于现代表格式,将数据视为一等原语,以确保我们内部数据转换平台(Slide 支持批量转换,Hugo 处理在线到数据湖的摄取)的一致性和性能。在此过程中,我们还构建了 UnifiedSparkCatalog,这是一个统一 Spark 目录,完全对用户隐藏表格式差异,我们将与本文一起开源。

The catalyst for change: Challenges with Hive Parquet

多年来,Hive Parquet 是我们数据湖的支柱,代表了绝大多数表。然而,随着数据量扩大,基于目录存储的架构局限性变得明显。我们确定了四个主要瓶颈:

  • Catalog latency: Hive Metastore (HMS) 成为集中式故障点。元数据访问的高并发导致 O(n) 列表开销,查询规划时间与分区数量线性增长,严重影响吞吐量。
  • The small file problem: 目录布局导致严重的文件碎片化。某些机器学习 (ML) 数据集的平均文件大小不足 1 MB,每个分区有数千个文件。在这种规模下,S3 对象列表和元数据请求延迟的开销推高了 Application Programming Interface (API) 成本,并减慢了扫描操作。
  • Operational toil: 数据工程师面临持续的手动分区注册开销。由于缺乏原生 ACID 支持(无原生 UPSERTDELETE),团队依赖复杂的变通方法来仔细管理数据变更。
  • The broken information loop: 目录和存储之间存在根本性断开。由于 HMS(而非存储层)被视为事实来源,直接修改 S3 经常导致目录过时,与磁盘上的实际状态不同步。

Why Iceberg? Strategic alignment and future-proofing

我们在选择 Apache Iceberg 作为默认格式之前评估了多个开放表格式。决定性因素归结为社区治理、引擎兼容性和长期灵活性。

最近的行业势头,包括对 Iceberg 不断增长的云原生支持,进一步验证了这一方向。我们正将 Grab 定位为长期格式无关,但 Iceberg 目前提供了最成熟的基础。

Comparison of Legacy Hive Parquet and Apache Iceberg

Adopting Iceberg at scale

迁移一个已建立的数据湖不是简单切换标志。我们的挑战是在绝大多数是 Hive Parquet、被许多引擎和团队查询的数据湖中推广 Iceberg,而不破坏依赖这些表的下游消费者。我们没有一次性转换所有内容,而是首先迁移最高价值的表。我们生产工作负载的效率提升非常显著。以下是代表性示例:

  • Query performance via Z-ordering: 在一个高流量的导航数据集上,我们实现了查询运行时间约 10 倍提升。Z-ordering 将具有相似值的行跨指定维度共置,使 Trino 能够利用数据跳过和 min/max 统计在查询规划期间修剪无关文件。这将查询运行时间从 70 秒减少到 6 秒。
  • S3 API cost reduction: 对于一个被大量查询的操作表,在不更改查询本身的情况下,每日 S3 API 成本最多降低 95%。更大的文件大小和在查询规划期间消除昂贵的对象列表是节省的主要原因。
  • Compute savings: 对于一个用于漏斗分析的数据集,我们将集群资源使用量减少了约一半。一个单独的 ML 特征管道也改善了下游模型的特征新鲜度。

The UnifiedSparkCatalog: Making mixed formats transparent

迁移到 Iceberg 解决了我们的存储和元数据问题,但在开发者体验层出现了新问题。现代表格式如 Delta、Iceberg 和 Hudi 各自实现了自己的自定义目录,扩展了 Spark 的 SessionCatalog。在标准 Spark 运行时中,只能将一个目录实现设置为默认 spark_catalog。支持其他格式需要显式目录声明,这意味着用户必须使用格式特定前缀引用表,如 iceberg_catalog.schema.tabledelta_catalog.schema.table

随着 Iceberg、Delta、Hudi 和 Hive 表现在共存,且表在格式之间主动迁移,这产生了两个问题:工程师必须知道他们查询的每个表的底层格式,且任何格式迁移都会静默破坏硬编码前缀的每个下游查询。

UnifiedSparkCatalog 是我们的解决方案。它是一个统一的 Spark 目录,抽象了使用混合表格式的复杂性,使用户无需考虑表使用的格式。我们从 Trino 的 Table Redirection 获得灵感,该功能在表格式与查询所用目录不同时透明地指向正确的连接器。我们的 Spark 等效实现如下:

How it works

  1. Table detection: 目录从 Hive Metastore 加载元数据。
  2. Format identification: 一个 TableTypeDetector 工具基于元数据属性(如 provider 字段)或基于路径的推断来识别格式。
  3. Operation routing: 目录将操作委托给正确的格式特定目录(Iceberg 的 SparkCatalog、Delta 的 DeltaCatalog 等),无需用户提供任何前缀。

Key design decisions

  • Lazy initialization: 每个格式的目录仅在首次需要时初始化,减少启动开销。如果格式的 JAR 不在类路径中,初始化会正常继续。目录只是跳过该格式,而不会使整个会话失败。
  • Naming as spark_catalog: 目录将其名称报告为 spark_catalog,因为 Spark 将此名称特殊对待,用于旧版 Hive Data Manipulation Language (DML) 操作。许多内部 Spark 代码路径检查此确切名称,以确定是否对插入、更新和删除使用 Hive 兼容逻辑。使用任何其他名称都会破坏旧版 Hive 表操作。
  • Catalog reuse: 在创建新目录实例之前,系统检查 Spark 的目录管理器中是否已存在一个。这保持了与插件如 OpenLineage 的兼容性,后者检查目录类类型以进行血缘提取。
  • Fallback behavior: 如果在预期的格式特定目录中找不到表,系统会回退到基础会话目录,确保对标准 Hive 表的健壮行为。

我们正在与本博客文章一起开源 UnifiedSparkCatalog。代码和文档可在此处获取 here

Lessons learned and overcoming hurdles

在大型生态系统中扩展 Iceberg 揭示了几个技术细微差别:

  • Hive lock contention: 我们在 HMS 中遇到了阻塞提交的 “zombie locks”。我们追踪到在高负载下元存储端的低读取超时。通过调整重试间隔和增加超时时限解决了该问题。
  • Timestamp handling: Spark 3.4 引入了 TIMESTAMP_NTZ(无时区),而 Iceberg 默认为 TIMESTAMP_LTZ(本地时区)。这导致与旧版 Hive 视图的兼容性问题。我们通过自定义迁移工作流和针对 Trino 部署的针对性补丁解决了该问题,以确保一致的转换。
  • Storage tier costs: 生成 Iceberg 元数据涉及读取历史数据,这可能触发一次性成本激增,因为文件在 S3 存储层之间移动。为了管理这一点,我们根据表的扫描频率和 API 操作成本优先进行迁移,而不是一次性迁移整个数据湖。

Conclusion: The road ahead

Apache Iceberg 现在是 Grab 数据策略的基础。它是 Slide 和 Hugo 的默认格式,且采用正在我们的计算平台中扩展。

展望未来,我们正在试验 Storage Partitioned Joins 以消除 Spark 中的 shuffle 阶段,并监控 Apache XTable 项目以保持格式之间的互操作性。我们的旅程不会止步于采用。我们将继续回馈生态系统,从即将发布的 UnifiedSparkCatalog 开始。

Acknowledgments: 这次旅程得益于 Grab 数据工程、基础设施以及搜索与个性化团队的共同努力。

Join us

Grab 是东南亚领先的超级应用,在八个国家(柬埔寨、印度尼西亚、马来西亚、缅甸、菲律宾、新加坡、泰国和越南)的 900 多个城市提供服务。通过单一平台,数百万用户可访问出行、配送和数字金融服务,包括网约车、外卖、支付、借贷以及通过 GXS Bank 和 GXBank 提供的数字银行服务。Grab 成立于 2012 年,其使命是通过为每个人创造经济赋权,同时实现可持续的财务表现和积极的社会影响,推动东南亚向前发展。

由技术驱动,由初心驱动,我们的使命是通过为每个人创造经济赋权,推动东南亚向前发展。如果这个使命与你共鸣,立即 加入我们的团队