在hive体系结构文章中我们说过hive的执行依赖于底层的MapReduce作业,因此对hadoop作业的优化或者对MapReduce作业的调整是提高hive性能的基础。如果没有适当的调整,即使查询hive中的一个小表,有时也会耗时数分钟才会得到结果。在此我们通过下面几种方式的调整,可以大大提高hive的性能。
1.启用压缩
压缩可以使磁盘上存储的数据量变小,对于文本文件格式能够压缩45%甚至更高的比例,这样可以通过降低I/O来提高查询速度。除非产生的数据用于外部系统,否则建议总是开启压缩策略。虽然压缩与解压缩会耗用一定的cpu资源,但hive产生的MapReduce作业一般都是IO密集型的,所以cpu开销一般不是问题。
我们可以通过 set io.compression.codecs;查看支持的压缩及解压缩算法,如没有需自行配置安装。
当一个复杂的hive查询提交后,会经历一系列的中间阶段的MapReduce作业,我们可以通过将这些中间运行中的数据压缩,来提高执行效率。在hive-site.xml 中通过设置hive.exec.compress.intermediate属性来启动中间数据压缩。
当然我们也可以在hive端使用set命令来设置:
当hive将输出写入到表时,输出的内容也是可以进行压缩的,我们可以通过hive.exec.compress.output属性启用输出压缩。
<property>
<name>hive.exec.compress.output</name>
<value>true</value>
</property>
或者
set hive.exec.compress.output=true;
set mapreduce.output.fileoutputformat.compress=true;
set mapreduce.output.fileoutputformat.compress.codec=org.apache.io.compress.SnappyCodec;
set mapreduce.output.fileoutputformat.compress.type=BLOCK;
2.优化连接
<1>自动map连接
当连接一个大表和一个小表时,如果启用了自动map连接,小标将保存在每个节点的本地缓存中,并在map阶段与大表进行连接。这样做有俩个明显的好处,首先是将小表装进缓存将节省每个节点上的数据读取时间,其次,避免了在hive查询中的数据倾斜,因为每个数据块的连接操作在map阶段已经完成。启用以下配置将开启自动map连接属性:
<property>
<name>hive.auto.convert.join</name>
<value>true</value>
</property>
<property>
<name>hive.auto.convert.join.noconditionaltask</name>
<value>true</value>
</property>
<property>
<name>hive.auto.convert.join.noconditionaltask.size</name>
<value>10000000</value>
</property>
<property>
<name>hive.auto.convert.join.use.nonstaged</name>
<value>true</value>
</property>
说明:
hive.auto.convert.join:是否启用基于输入文件的大小,将普通连接转化为Map连接的优化机制。
hive.auto.convert.join.noconditionaltask:是否启用基于输入文件的大小,将普通连接转为map连接的
优化机制,假如参与连接的表(或者分区)有n个,如果打开这个参数,此时如果n-1个表(或分区)的总
大小小于hive.auto.convert.join.noconditionaltask.size指定的值,那么会将次查询转化为Map
连接。
hive.auto.convert.join.noconditionaltask.size:如果参与连接的N个表中的N-1个总大小小于此参数
值,直接将连接转为map连接。默认值为10M.
hive.auto.convert.join.use.nonstaged:对于条件连接,如果从一个小的输入流可以应用于join操作,而
不需要过滤,那么不需要通过MapReduce的本地任务在分布式缓存中预存。注意,该参数在
vectorization或者tez执行引擎中是不起作用的。
<2>倾斜连接
当俩个大表进行连接时,会先基于连接键分别对两个表进行排序,然后再进行join连接。Mapper将特定键值的所有行发送给同一个Reducer。例如,假如表A有id为1,2,3,4,5五个值,而表B有id为1,2,3,4四个值。当我们有如下查询语句时:select A.ID FROM A JOIN B ON A.ID=B.ID.假设在这个查询中id=1的数据是倾斜的,那么当其他Mapper发送给Reducer的数据都完成后,而id=1对应的Reducer则需要很长时间才能完成,这将拖累整个查询的效率。
配置优化倾斜连接的相关属性如下:
<property>
<name>hive.optimize.skewjoin</name>
<value>true</value>
</property>
<property>
<name>hive.skewjoin.key</name>
<value>100000</value>
</property>
<property>
<name>hive.skewjoin.mapjoin.map.tasks</name>
<value>20000</value>
</property>
<property>
<name>hive.skewjoin.mapjoin.min.split</name>
<value>33556872</value>
</property>
参数说明:
hive.optimize.skewjoin:是否为join中的倾斜键创建单独的执行计划。它基于存储在元数据中的倾斜键。在编译时,Hive为倾斜键和其他键值生成各自的查询计划。
hive.skewjoin.key:决定如何确定连接中的倾斜键。在连接操作中,如果同一键值所对应的数据行数超过该参数值,则认为该键是一个倾斜连接键。
hive.skewjoin.mapjoin.map.tasks:指定倾斜连接中,用于Map连接作业的任务数。该参数应该与hive.skewjoin.mapjoin.min.split一起使用,用来执行粒度的控制。
hive.skewjoin.mapjoin.min.split:通过指定最小split的大小,确定map连接作业的任务数。该参数应该与>hive.skewjoin.mapjoin.map.tasks一起使用,执行粒度的控制。
<3>桶map连接
如果连接中使用的表是按特定列分桶的,可以开启桶Map连接来提高性能。
<property>
<name>hive.optimize.bucketmapjoin</name>
<value>true</value>
</property>
<property>
<name>hive.optimize.bucketmapjoin.sortedmerge</name>
<value>true</value>
</property>
参数说明:
hive.optimize.bucketmapjoin:是否尝试桶Map连接。
hive.optimize.bucketmapjoin.sortedmerge:是否尝试在桶map连接中使用归并扫描。
3.避免使用order by全局排序
hive中使用order by排序只会用一个reducer进行排序,以达到全局排序。对于大数据集来说,这种做法是相当耗费时间。我们可以使用sort by 子句为每个reducer生成一个排好序的文件。如果需要控制一个特定的数据行流向哪个reducer,可以使用distribute by 子句,如:
select id,name,class from class_data distribute by class sort by id desc;
4.优化limit操作
默认时limit操作是执行整个查询,然后返回有一定限定的行。在某些情况下,这种处理方式比较浪费,我们可以通过设置下面的属性来避免此行为。
<property>
<name>hive.limit.optimize.enable</name>
<value>true</value>
</property>
<property>
<name>hive.limit.row.max.size</name>
<value>100000</value>
</property>
<property>
<name>hive.limit.optimize.limit.file</name>
<value>10</value>
</property>
<property>
<name>hive.limit.optimize.fetch.max</name>
<value>30000</value>
</property>
参数说明:
hive.limit.optimize.enable:是否启用limit优化。当使用limit语句时,对源数据进行抽样。
hive.limit.row.max.size:在使用limit做数据的子集查询时,保证最小的行数。
hive.limit.optimize.limit.file:在使用limit做数据子查询时,采样的最大文件数。
hive.limit.optimize.fetch.max:使用简单limit数据抽样时,允许的最大行数
5.启用Tez执行引擎
使用Tez执行引擎代替传统的MapReduce引擎会大幅提升hive的查询性能。配置Tez执行引擎如下:
<property>
<name>hive.execution.engine</name>
<value>tez</value>
</property>
6.启用并行执行
每个hiveQL都被转换成一个或多个阶段,可能是一个MapReduce阶段,采样阶段,归并阶段等。默认情况下hive在任意时刻只执行其中的一个阶段。如果组成一个特定作业的多个执行阶段是彼此独立的,那么它们可以并行执行。从而加快作业执行速度。通过配置以下选项开启并行执行。
<property>
<name>hive.exec.parallel</name>
<value>true</value>
</property>
<property>
<name>hive.exec.parallel.thread.number</name>
<value>5</value>
</property>
参数说明:
hive.exec.parallel:是否并行执行作业
hive.exec.parallel.thread.number:最多可以并行执行多少个作业数。
7.启用MapReduce严格模式:
hive提供一个严格的模式,可以防止用户执行那些可能产生负面影响的查询。通过设置下面的属性启用MapReduce严格模式。
<property>
<name>hive.mapred.mode</name>
<value>strict</value>
</property>
注意:
此模式下禁止3种类型的查询:
1.对于分区表,where子句不包含分区字段过滤条件的查询语句不允许执行。
2.对于使用order by子句的查询,要求必须使用limit子句,否则不允许执行
3.限制笛卡尔积查询。
8.使用单一reduce执行多个Group by
通过为group by 开启单一reduce任务属性,可以将一个查询中的多个group by操作联合在一起发送给单一MapReduce作业。
<property>
<name>hive.multigroupby.singlereducer</name>
<value>true</value>
</property>
9.使用ORC文件格式
hive的orc文件格式可以有效提升hive查询性能。以下是一个hive不同文件格式大小的对比图:
10.使用向量化查询
通过查询执行向量化,使hive从单行处理数据改为批量处理方式,具体来说是一次处理1024行而不是原来的每次只处理一行,这大大提升了指令流水线和缓存利用率,从而提高了表扫描、聚合、过滤和连接等操作的性能。通过以下配置启用向量化:
<property>
<name>hive.vectorized.execution.enabled</name>
<value>true</value>
</property>
<property>
<name>hive.vectorized.execution.reduce.enabled</name>
<value>true</value>
</property>
<property>
<name>hive.vectorized.execution.reduce.groupby.enabled</name>
<value>true</value>
</property>
参数说明如下:
hive.vectorized.execution.enabled:如果该标志设置为true, 则开启查询执行的向量模式,默认值为false.
hive.vectorized.execution.reduce.enabled:如果该标志为true,则开启查询执行reduce端的向量模式,默认值为true
hive.vectorized.execution.reduce.groupby.enabled:如果该标志为true,则开启查询执行reduce端的group by操作的向量模式,默认值为true.