Uber 应用分享 | 使用 Parquet Page Index 加速 Presto 查询
admin
2024-03-06 09:35:05
0

引言

当前,数据量呈快速增长态势,给诸如 Presto 等查询引擎带来了挑战。
Presto 作为一种流行的交互式查询引擎,具有可扩展、高性能并可与 Hadoop 进行平滑集成的特性。随着数据量的增长,Presto 需要读取更大的数据块并将其加载到内存中,继而导致 IO、内存占用增大以及 GC 时间变长等。
Apache Parquet 是一种可用于高效存储和检索数据的开源列式文件格式,提供高效的数据压缩和编码方案,性能更优,能批量处理复杂的数据。

我们先前已经采取了一些措施来加快 Presto 对 Parquet 数据的读取速度,但需要读取的数据量依旧很大。从 Java 版本的 Parquet (parquet-mr 1.11.0) 开始,Parquet 添加了一个名为 Page Index 的特性,通过在列块(column chunk) 中过滤不必要的 Parquet 页 (page) 来加快查询速度。

本文就该特性、移植到 Presto 的状态以及基准测试结果进行了介绍。

统计信息

Parquet 文件元数据包含有关文件中数据的最小 / 最大值的统计信息。对于一个给定 filter 的查询而言,统计信息的最小 / 最大值可以用作 filter 要查找的值的范围。如果要查找的值不在范围内,就可以跳过读取该数据块。这样一来提高了 IO、内存和 CPU 等资源的利用率,从而加快了查询速度。

下述示例展示了如何将 filter 应用于包含统计信息的表,‘x > 100’的 filter 会查找 (100, ∞) 范围内的值。表中的统计信息显示只有第二列(最小值为 1 和最大值为 209) 的数值范围 [1, 209],与 filter 的过滤范围重叠。因此,我们可以跳过读取剩余 3 列,从而将数据读取的时间减少 3/4 。

Parquet Page Index

Parquet 包含列块和页的统计信息。列块的统计信息显示出该列块中数据的取值范围,而页统计信息显示的是页数据,粒度更细、更有效。下述示例展示了页统计信息与列统计信息相比,如何能更好地降低数据读取量。

在第一个示例中,有一个查询中包含了 “x = 2000” 的 filter 条件。为便于演示,我们仅在一个列块中显示三个页。图表中的三个页的统计信息构成了三个范围:[-100, 60]、[50, 234] 和 [800, 1000] 。列块的统计信息构成 [-100, 1000] 的范围。由于我们正在查找的值为 2000 ,因此页统计信息和列块统计信息都显示无需读取该列中的数据。在这种情况下,页统计信息和列块统计信息一样,都有效地减少了数据读取量。

在第二个示例中,列块和页中的数据和统计信息与第一个示例相同。唯一不同的是 filter “x = 55”。在这种情况下,由于该值在列块统计范围 [-100, 1000] 内,因此对于读取的判断为 “yes”。同样地,第一个页统计范围 [50, 234] 和第二个页统计范围 [-100, 60] 对于读取的判断都为 “yes”,但对于最后一个页而言,由于超出了 [800, 1000 ] 的范围,因此读取判断为 “no”。

在第三个示例中,列块和页中的数据和统计信息依旧与第一个示例相同,但 filter 更改为 'x = 700'。该值在列块统计范围 [-100, 1000] 内,因此对于读取的判断为 “yes”,但对于所有页统计信息而言,由于所有的范围都不包含要查找的值 “700”,因此读取的判断结果为 “No”, 跳过整列数据,不予读取。

在所有上述示例中,我们在一个列块中只显示 3 个页,但实际上一个列块中通常有几十甚至几百个页。因此,在现实操作中能节省更多的成本。如果列数据是经过排序的,那么误报的可能性就会降低,从而使得页统计效果更好。

Presto 的页统计

在 1.11.0 版本之前,页统计信息都放在页头(page header)中。问题是要获取页统计信息必须读取每一个页。即便之后判定无需读取该页,但该页已经被读取。为了解决该问题,parquet-mr 1.11.0 将列块的所有页统计信息放在同一位置,方便 reader 可以一次性读取并判定应该读取哪个页。由于 Presto 部分重写了 parquet-mr 代码,我们需要将 Parquet 1.11.0 中的修改移植到 Presto 代码库中。代码修改 (PR-17284) 已于 2022 年 2 月合并到 Presto master 分支中,并将在 0.273 版本中发布。

基准测试结果

我们基于需要频繁访问的生产数据表对 Parquet 列索引进行了基准测试。我们用原始生产环境的数据表的快照创建了一个测试表,它包含了若干数据分区,并且数据是经过排序的。之后,我们在测试环境中对表运行 Presto 查询。可以针对每个查询通过 Presto 会话属性设置打开和关闭 Page Index 功能,从而实现横向比较。

我们观察到,输入数据扫描存在巨大的优化空间。下图显示了 Parquet Page Index 启用(左)与禁用(右)时运行示例查询的统计信息。可以看出,他们在这一阶段生成了相同的数据,但是在未启用 Parquet 列索引(column index) 时需要扫描 149.31MB/239K 行原始输入数据,而在启用 Parquet 列索引的情况下只需要扫描 63.31MB/75.4K 行原始输入数据。后者的输入读取量降低了 57%。这是因为在使用 Parquet Page Index 时,operator 可以识别并跳过不符合过滤条件的页。

除了在原始输入读取量方面的提升外,我们还从测试中观察到内存使用量也有所降低。从下面的截图可以看出,在启用 Parquet Page Index(右)时,只需要使用 91.73MB 的峰值内存,而在未启用 Parquet Page Index 时,则需要使用 141.60 MB 的峰值内存。实现此项提升在预期范围内:由于查询需要读取的原始输入数据更少,因此保存数据所需的内存也更小。

值得注意的是,我们测试用的 Presto 查询在经过排序的列上使用 filter,例如:WHERE foo = bar,其中 foo 列是有序的,这也是 Parquet Page Index 降低读取量效果最显著的地方,如果不对 filter 依赖的列数据进行排序,则收益可能降低。

相关内容

热门资讯

linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
C++ 机房预约系统(六):学... 8、 学生模块 8.1 学生子菜单、登录和注销 实现步骤: 在Student.cpp的...
A.机器学习入门算法(三):基... 机器学习算法(三):K近邻(k-nearest neigh...
数字温湿度传感器DHT11模块... 模块实例https://blog.csdn.net/qq_38393591/article/deta...
有限元三角形单元的等效节点力 文章目录前言一、重新复习一下有限元三角形单元的理论1、三角形单元的形函数(Nÿ...
Redis 所有支持的数据结构... Redis 是一种开源的基于键值对存储的 NoSQL 数据库,支持多种数据结构。以下是...
win下pytorch安装—c... 安装目录一、cuda安装1.1、cuda版本选择1.2、下载安装二、cudnn安装三、pytorch...
MySQL基础-多表查询 文章目录MySQL基础-多表查询一、案例及引入1、基础概念2、笛卡尔积的理解二、多表查询的分类1、等...
keil调试专题篇 调试的前提是需要连接调试器比如STLINK。 然后点击菜单或者快捷图标均可进入调试模式。 如果前面...
MATLAB | 全网最详细网... 一篇超超超长,超超超全面网络图绘制教程,本篇基本能讲清楚所有绘制要点&#...
IHome主页 - 让你的浏览... 随着互联网的发展,人们越来越离不开浏览器了。每天上班、学习、娱乐,浏览器...
TCP 协议 一、TCP 协议概念 TCP即传输控制协议(Transmission Control ...
营业执照的经营范围有哪些 营业执照的经营范围有哪些 经营范围是指企业可以从事的生产经营与服务项目,是进行公司注册...
C++ 可变体(variant... 一、可变体(variant) 基础用法 Union的问题: 无法知道当前使用的类型是什...
血压计语音芯片,电子医疗设备声... 语音电子血压计是带有语音提示功能的电子血压计,测量前至测量结果全程语音播报࿰...
MySQL OCP888题解0... 文章目录1、原题1.1、英文原题1.2、答案2、题目解析2.1、题干解析2.2、选项解析3、知识点3...
【2023-Pytorch-检... (肆十二想说的一些话)Yolo这个系列我们已经更新了大概一年的时间,现在基本的流程也走走通了,包含数...
实战项目:保险行业用户分类 这里写目录标题1、项目介绍1.1 行业背景1.2 数据介绍2、代码实现导入数据探索数据处理列标签名异...
记录--我在前端干工地(thr... 这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前段时间接触了Th...
43 openEuler搭建A... 文章目录43 openEuler搭建Apache服务器-配置文件说明和管理模块43.1 配置文件说明...