1
背景
Apache Hive是Hadoop之上最流行的数据仓库引擎。提升Hive性能的功能可以显著提高集群资源的整体利用率。Hive使用一连串的运算符来执行查询。这些运算符包括MapTask,ReduceTask或SparkTask,它们在查询执行计划中进行调度。以前这些运算符被设计为每次处理一行数据。一次处理一行导致运算符效率不高,因为需要许多虚函数调用来处理扫描的每一行。另外,如果运算符一次只处理一行,不能利用CPU的SIMD指令集(例如SSE或AVX)进行加速。本文主要介绍如何在Hive中利用基于SIMD的优化,使Apache Parquet表的查询运行效率提升26%以上。
2
CPU矢量化
矢量化是将算法从一次操作一个值转换为一次操作一组值的过程。现在的CPU一般都直接支持矢量操作,即使用单个指令处理多个数据点(SIMD)。
上图显示了使用scalar和vector指令添加两组值的简单示例
例如,支持AVX-512指令集的CPU提供512位寄存器,与16个标量指令中的相同计算相比,它可以保存多达16个32位的值并执行简单操作如在一条指令中执行加法运算。在此示例中,矢量化(vectorized)执行将比标量(scalar )执行快16倍。
3
Hive中的矢量化
为了利用这些优化,Hive在HIVE-4160中引入了矢量化查询执行,参考:
https://issues.apache.org/jira/browse/HIVE-4160
矢量化查询执行引入了新的运算符和表达式,即每次处理一批行,而不是每次只处理一行数据。与基于行的执行相比,矢量化执行避免了大量的虚函数调用,从而提高了指令和数据缓存命中率。它更好地利用了现在的CPU(如 Intel Xeon Scalable processors)的指令流水线(instruction pipeline),还可以利用Intel SSE/AVX指令集在CPU级别并行化数据处理。这可以显著提高查询性能。关于Hive中矢量化的更多设计细节可以参考:
https://issues.apache.org/jira/secure/attachment/12603710/Hive-Vectorized-Query-Execution-Design-rev11.pdf
4
Parquet Vectorized Reader
Apache Parquet是大数据生态系统中广泛使用的列式文件格式。但是Hive却不能矢量化读取Parquet文件,意味着即使你的集群中启用了矢量化,map任务在读取Parquet文件时依旧会一次只处理一行。所以如果你的表使用的是Parquet文件格式,查询这些表的时候将不能利用矢量化查询执行来提升性能。为了改善这一点,Cloudera和英特尔密切合作,在HIVE-14826中引入了Hive Parquet Vectorization,参考:
https://issues.apache.org/jira/browse/HIVE-14826
Parquet vectorized reader一次返回一批行的列而不是只有一行,这一批列可以直接被传递给运算符树(operator tree),而不用做任何中间转换。在Hive中而不是Parquet库中实现vectorized parquet reader可以避免额外的内存复制操作来创建批次,从而进一步提高了性能。从CDH6.0开始,CDH中的Hive可以使用此功能。
5
已知的限制
除了像string,integer或double这样的基本数据类型之外,Parquet还支持struct,list或map等复杂类型。目前vectorized reader只能处理基本数据类型和不带嵌套的复杂类型。支持嵌套复杂类型处理的工作尚在进行中。当查询的数据是嵌套复杂类型时(如list,map或struct),查询引擎会降回使用非矢量化执行。矢量化执行所有支持的数据类型参考:
http://www.cloudera.com/documentation/enterprise/latest/topics/hive_query_vectorization.html
6
使用Parquet矢量化
CDH6.0默认开启了Hive矢量化,你也可以在连接会话中使用set将
hive.vectorized.execution.enabled
配置为true,该参数默认值也为true从CDH6.0开始。
代码语言:javascript复制set hive.vectorized.execution.enabled=true;
你可以通过配置
hive.vectorized.input.format.excludes
来控制是否对某些文件格式不启用矢量化,配置该参数的值需要使用文件格式的类名的全名,采用逗号分隔,然后被配置的文件格式将都不会进行矢量化计算。例如,要为Parquet表禁用矢量化,可以将
hive.vectorized.input.format.excludes
设置为
org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat。
通过Cloudera Manager可以为Hive服务配置这些。更多与矢量化相关的配置具体参考:
http://www.cloudera.com/documentation/enterprise/latest/topics/hive_query_vectorization.html
7
性能结果
我们使用Hive on Spark在4个节点的Skylake集群(Xeon Gold 6140)上测试了Parquet矢量化的性能,测试场景是使用TPC-DS,数据集为3TB。同时使用CDH5.15.1和CDH6.0来比较不同版本的CDH的性能差异。以下是具体的硬件和软件配置:
Configuration A是使用CDH5.15.1运行基线的集群,不包括Parquet矢量化功能。 Configuration B使用CDH6.0,配置中禁用Parquet矢量化。Configuration C也使用CDH6.0,但启用了Parquet矢量化。共运行了TPC-DS的55个查询。
结果显示通过此功能可以带来显著的性能提升。使用CDH6.0中的Parquet矢量化功能,所有查询时间的几何平均(geomean )从CDH5.15.1的243.82秒提高到176.70秒,比CDH5.15.1提高了1.36倍。
下图显示同样在CDH6.0中,与禁用Parquet矢量化相比,开启矢量化后对于TPC-DS各个查询的性能提升百分比。5个查询(q21,q39,q58,q66,q88)性能提升超过50%,其余大部分提升在20%-40%之间。
8
总结
CDH6.0中的性能基准测试表明启用Parquet矢量化后可以显著提升典型的ETL工作负载的性能。使用TPC-DS,启用Parquet矢量化可以使平均性能提升26.5%(所有查询运行时间的几何平均值)。Vectorization通过减少虚函数调用的数量,并利用CPU的SIMD指令来获得这些性能提升。当满足某些条件(如受支持的字段类型或表达式),使用Hive查询就会使用矢量化执行。如果查询不能使用矢量化,则会回退到非矢量化执行。总的来说,从CDH6.0开始,在如今主流的处理器上,启用Parquet矢量化对于你查询Parquet表时都可以实现比以前更好的查询性能。
原文参考:
https://blog.cloudera.com/blog/2018/12/faster-swarms-of-data-accelerating-hive-queries-with-parquet-vectorization/
作者列表:
VihangKarajgaonkar is a Software engineer at Cloudera
SantoshKumar is a Senior Product Manager at Cloudera
HaifengChen is a Senior Engineering Manager at Intel
Cheng Xuis an Engineering Manager at Intel
WangLifeng is a Software Engineer at Intel