Impala如何将Iceberg上的查询编译性能提升12倍

Impala如何将Iceberg上的查询编译性能提升12倍

原文作者:Riza Suminto
原文链接:https://blog.cloudera.com/12-times-faster-query-planning-with-iceberg-manifest-caching-in-impala/
译者:stiga-huang

Apache Iceberg 是一种新兴的开放表格式,专为大规模分析场景而设计。Iceberg 项目持续以 Java 库的形式开发 Iceberg 格式的实现。 Impala、Hive、Spark、Trino 等多个计算引擎都采用Iceberg的 Java 库来支持Iceberg 表格式的查询。

不同的查询引擎(例如 Impala、Hive 和 Spark)可以直接从Iceberg Java 库中受益—— 各种Iceberg表的分析操作,如列出数据文件、选择snapshot、分区过滤和谓词过滤都可以通过Iceberg Java API来进行,不需要每个查询引擎自己实现。 然而,Iceberg Java API 调用并不总是很高效。

本文将讨论 Cloudera 在元数据读取方面为 Apache Iceberg 项目做出的性能改进,并将展示使用 Apache Impala 作为查询引擎的性能优化。其他查询引擎(如 Hive 和 Spark)也可以从 Iceberg 的改进中受益。

Impala + Iceberg反复读取元数据的问题

Apache Impala 是一个开源、分布式、基于MPP架构实现的 SQL 查询引擎。Impala 中有两个组件:BE (Backend/后端) Executor和FE (Frontend/前端) Planner。 Executor是用 C++ 编写的,以提供高效的执行性能;FE Planner则是用Java编写的,负责分析用户的SQL查询并生成查询计划。 在生成查询计划(即Planning)期间,Impala FE需要分析表的元数据,如分区信息、数据文件和统计信息,以得出优化后的执行计划。 由于Impala FE是用Java编写的,可以通过Iceberg的Java 库直接获取Iceberg表的元数据。
Impala如何将Iceberg上的查询编译性能提升12倍_第1张图片
Hive表格式中,分区信息、统计信息等元数据存储在Hive Metastore(HMS)中。由于 HMS 的后端存储是 RDBMS(如 mysql、 postgresql等),Impala 可以快速访问 Hive 表元数据。另外Impala 还将元数据缓存在 CatalogD 和 Coordinator 的内存中,如果之前访问过目标表的元数据和文件元数据,则元数据的分析会更快。 这种缓存很有用,因为Impala可能会在并发查询或者在单个查询中多次分析同一张表。 上图显示了 TPC-DS Q9 的查询计划,其中对一个公用表 store_sales 进行了 15 次分析(使用不同的filter),以生成 15 个单独的table scan fragment。
Impala如何将Iceberg上的查询编译性能提升12倍_第2张图片
Iceberg 表格式的特别之处在于,其将元数据也存储为文件,跟数据文件放在一起。 图 2 展示了三种元数据文件:Snapshot文件、Manifest list和Manifest文件。 Iceberg 格式的数据文件和元数据文件是不可变的。 对 Iceberg 表的 DDL/DML 操作将创建一组新文件,而不是重写或替换先前的文件。 通过 Iceberg Java API 进行的元数据查询都要读取这些元数据文件的子集。 因此,即使不同的API调用分析的是同一张表,它们都会产生额外的存储延迟和网络延迟开销。 IMPALA-11171 中描述了此问题。下图统计了TPC-DS Q9在Planning阶段的socket读操作,使用的是S3上的Iceberg表。
Impala如何将Iceberg上的查询编译性能提升12倍_第3张图片

Iceberg Manifest 缓存设计

Impala FE在使用 Iceberg Java API 时要留意前述的开销,以保持快速的Planning性能。 减少 Iceberg 元数据文件的多次远程读取,需要实现Impala 对 Hive 表格式类似的缓存策略,这个缓存可以放在Impala FE或者嵌入在 Iceberg Java 库中。 最终,我们选择后者,这样可以贡献一些对整个社区有用的东西,所有计算引擎都可以从中受益。 尽可能减少侵入性地实现这种缓存机制也很重要。
Impala如何将Iceberg上的查询编译性能提升12倍_第4张图片
我们在Iceberg 项目中提交了 Pull request 4518 来实现这种缓存机制。 这个想法是将Manifest文件(Iceberg 元数据文件层次结构的最底层)的二进制内容缓存在内存中,并让 Iceberg Java 库从内存中读取(如果缓存命中),而不是再次从文件系统中读取。 得益于Iceberg 元数据文件(包括Manifest文件)的不可变性,我们不用处理缓存失效的问题。因此,Iceberg Java 库只会从文件系统中读取新的Manifest文件,用新内容填充Manifest缓存,并在此过程中使旧内容过期。

上图说明了 Iceberg Manifest缓存的两层设计。 第一层是FileIO级缓存,将FileIO映射到它自己的ContentCache中。 FileIO 本身是核心 Iceberg 库和底层存储之间的主要接口。 Iceberg 库的任何文件读写操作都将通过 FileIO 接口。 默认情况下,第一层缓存最多有 8 个 FileIO可以同时在内存中包含 ContentCache。 可通过 Java 系统属性iceberg.io.manifest.cache.fileio-max 增加此数量。

第二层缓存是ContentCache对象,它是文件路径到二进制文件内容的映射。 二进制文件内容作为 4MB 块的 ByteBuffer 存储在内存中。 这两层都是使用 Caffeine 库实现的,Caffeine 库是一个高性能缓存库,已被 Iceberg Java 库使用,并结合了weak keys和soft values。 这种组合允许在 JVM 垃圾回收FileIO时自动删除缓存条目。 这个缓存是在核心 Iceberg Java 库 (ManifestFiles.java) 中实现的,可供不同的 FileIO 实现直接使用,无需更改代码。

Impala Coordinator 和 CatalogD 可以使用 Iceberg Manifest缓存对 Iceberg 表做快速的file listing和过滤无关文件。 值得注意的是,Iceberg Manifest缓存并不会替代 CatalogD 和 Coordinator 已有的Catalog缓存。 一些表元数据,如表结构定义、文件描述符和Block Location(HDFS 独有)仍然缓存在 CatalogD 内。 CatalogD 缓存和 Iceberg Manifest缓存是相互独立的优化,一起助力 Impala 实现快速的Query Planning。下表总结了元数据缓存的分类:

表类型 CatalogD 缓存 Iceberg Manifest 缓存(位于Coordinator and CatalogD中)
Hive表格式 表结构定义、File Descriptor、HDFS Block Location
Iceberg表格式 表结构定义、File Descriptor、HDFS Block Location Manifest文件内容

不同的Iceberg Catalog可以通过它们自己的属性来配置对应FileIO的ContentCache。每个属性的描述和默认值如下:

# 是否启用Manifest缓存
iceberg.io.manifest.cache-enabled=true;
# 缓存的过期时间
iceberg.io.manifest.cache.expiration-interval-ms=60000;
# 允许缓存的最大Manifest文件长度
iceberg.io.manifest.cache.max-content-length=8388608;
# 配置缓存的总大小
iceberg.io.manifest.cache.max-total-bytes=104857600;

性能测试

Impala如何将Iceberg上的查询编译性能提升12倍_第5张图片
从Iceberg 1.1.0 开始,Impala 和其他查询引擎都可以使用Manifest缓存功能。 上图显示了 Impala Planning时间的改进。 x 轴表示 TPC-DS中查询的百分比,y 轴表示查询编译(Planning)时间(毫秒)。 与没有Manifest缓存的 Iceberg(Vanilla Iceberg)相比,启用 Iceberg Manifest缓存可以将查询编译速度提高 12 倍(Iceberg + caffeine),几乎与 Hive 外部表的性能相当。 在配置方面,我们将 Coordinator 中的 io.manifest.cache.expiration-interval-ms增加到一小时。 这对 Impala 是有利的,因为对于HiveCatalog(默认的Iceberg Catalog实现),Impala FE持有一个常驻的单例。 另外正如前面所述, Iceberg 文件是不可变的,因此设置较长的过期时间也是可以的。 较长的过期时间还将使缓存的寿命更长,并可用于同一张表上的多个查询。

Impala 从 core-site.xml 读取默认的 Iceberg Catalog配置。 要配置Iceberg Manifest缓存使用一小时的过期时间,可在 Coordinator 和 CatalogD 的 core-site.xml 中如下配置:

  <property>
    <name>iceberg.io-implname>
    <value>org.apache.iceberg.hadoop.HadoopFileIOvalue>
  property>
  <property>
    <name>iceberg.io.manifest.cache-enabledname>
    <value>truevalue>
  property>
  <property>
    <name>iceberg.io.manifest.cache.max-total-bytesname>
    <value>104857600value>
  property>
  <property>
    <name>iceberg.io.manifest.cache.expiration-interval-msname>
    <value>3600000value>
  property>
  <property>
    <name>iceberg.io.manifest.cache.max-content-lengthname>
    <value>8388608value>
  property>

总结

Apache Iceberg 是一种新兴的开放表格式,专为大规模分析场景而设计。 Iceberg 项目持续以 Java 库的形式开发 Iceberg 格式的实现,该库已被许多查询引擎(例如 Impala、Hive 和 Spark)采用。 Cloudera 为 Apache Iceberg 贡献了 Iceberg Manifest缓存功能,以减少重复读取Manifest文件的问题。 我们通过使用 Apache Impala 作为查询引擎来展示 Iceberg Manifest缓存的优势,并展示在启用 Iceberg Manifest缓存的情况下,Impala 在 Iceberg 表上的查询编译(Planning)时间能获得高达 12 倍的加速。

更多问题可在Impala社区微信群讨论(加微信号China_Impala进群)。

你可能感兴趣的:(Impala,impala,大数据)