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表的元数据。
TPC-DS Q9需要反复分析store_sales表(使用不同filter)
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。
Iceberg API会读取元数据文件
Iceberg 表格式的特别之处在于,其将元数据也存储为文件,跟数据文件放在一起。 图 2 展示了三种元数据文件:Snapshot文件、Manifest list和Manifest文件。 Iceberg 格式的数据文件和元数据文件是不可变的。 对 Iceberg 表的 DDL/DML 操作将创建一组新文件,而不是重写或替换先前的文件。 通过 Iceberg Java API 进行的元数据查询都要读取这些元数据文件的子集。 因此,即使不同的API调用分析的是同一张表,它们都会产生额外的存储延迟和网络延迟开销。 IMPALA-11171 中描述了此问题。下图统计了TPC-DS Q9在Planning阶段的socket读操作,使用的是S3上的Iceberg表。
TPC-DS Q9在Planning阶段的socket读操作,使用的是S3上的Iceberg表

Iceberg Manifest 缓存设计

Impala FE在使用 Iceberg Java API 时要留意前述的开销,以保持快速的Planning性能。 减少 Iceberg 元数据文件的多次远程读取,需要实现Impala 对 Hive 表格式类似的缓存策略,这个缓存可以放在Impala FE或者嵌入在 Iceberg Java 库中。 最终,我们选择后者,这样可以贡献一些对整个社区有用的东西,所有计算引擎都可以从中受益。 尽可能减少侵入性地实现这种缓存机制也很重要。
Iceberg Manifest缓存实现
我们在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 LocationManifest文件内容

不同的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;

性能测试

实验结果
从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-impl</name>
    <value>org.apache.iceberg.hadoop.HadoopFileIO</value>
  </property>
  <property>
    <name>iceberg.io.manifest.cache-enabled</name>
    <value>true</value>
  </property>
  <property>
    <name>iceberg.io.manifest.cache.max-total-bytes</name>
    <value>104857600</value>
  </property>
  <property>
    <name>iceberg.io.manifest.cache.expiration-interval-ms</name>
    <value>3600000</value>
  </property>
  <property>
    <name>iceberg.io.manifest.cache.max-content-length</name>
    <value>8388608</value>
  </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进群)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Impala 是一个高性能、分布式的 SQL 查询引擎,它运行于 Apache Hadoop 生态系统之上。在使用 Impala 进行查询时,可能会遇到死锁的情况。死锁是指多个进程或线程因竞争资源而无法继续执行的情况。 要解决 Impala 查询死锁问题,可以采取以下几种方法: 1. 确认是否真的是死锁:首先需要确认是否真的发生了死锁,可以通过查看 Impala 的日志文件来确定。在日志中搜索关键字 "deadlock" 或者 "lock wait timeout" 可能会得到一些有用的信息。 2. 找出导致死锁的查询语句:确定哪些查询语句导致了死锁,可以通过查看 Impala查询执行计划、查询日志或者监控工具来获取相关信息。 3. 优化查询语句:针对导致死锁的查询语句进行优化,有可能通过改变查询顺序或者使用不同的查询策略来避免死锁的发生。 4. 调整 Impala 配置:根据具体情况,可能需要调整 Impala 的配置参数,例如增加并发度、调整资源分配等。 5. 并发控制:在多用户环境中,可以通过实施合适的并发控制机制来避免死锁,例如使用事务隔离级别、加锁机制等。 6. 升级 Impala 版本:如果遇到的是已知的死锁问题,有可能在较新的 Impala 版本中已经得到修复。 需要注意的是,死锁问题往往比较复杂,解决方案可能因具体情况而异。建议在遇到死锁问题时,深入分析具体情况,并根据实际情况采取相应的解决措施。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值