11.6 Hive的优化

Hive针对不同的查询进行优化,其优化过程可以通过配置进行控制。本节我们将介绍部分优化策略及优化控制选项。

1.列裁剪(Column Pruning)

在读取数据时,只读取查询中需要用到的列,而忽略其他列,例如如下查询:


SELECT a, b FROM t WHERE e<10;


其中,对于表t包含的5个列(a, b,c, d,e),经过列裁剪,列c和d将会被忽略,执行中只会读取a, b,e列。要实现列裁剪,需要设置参数hive.optimize.cp=true。

2.分区裁剪(Partition Pruning)

在查询过程中减少不必要的分区,例如如下查询:


SELECT*FROM(SELECT c1,COUNT(1)

FROM T GROUP BY c1)subq

WHERE subq.prtn=100;

SELECT*FROM T1 JOIN

(SELECT*FROM T2)subq ON(T1.c1=subq.c2)

WHERE subq.prtn=100;


经过分区裁剪优化的查询,会在子查询中就考虑subq.prtn=100条件,从而减少读入的分区数目。要实现分区裁剪,须设置hive.optimize.pruner=true。

3.Join操作

当使用有Join操作的查询语句时,有一条原则:应该将条目少的表/子查询放在Join操作符的左边。原因是在Join操作的Reduce阶段,Join操作符左边表中的内容会被加载到内存中,将条目少的表放在左边可以有效减少发生内存溢出(OOM:Out of Memory)的几率。

对于一条语句中有多个Join的情况,如果Join的条件相同可以进行优化,比如如下查询:


INSERT OVERWRITE TABLE pv_users

SELECT pv.pageid, u.age FROM page_view p

JOIN user u ON(pv.userid=u.userid)

JOIN newuser x ON(u.userid=x.userid);


我们可以进行的优化是,如果Join的key相同,那么不管有多少个表,都会合并为一个MapReduce。如果Join的条件不相同,比如:


INSERT OVERWRITE TABLE pv_users

SELECT pv.pageid, u.age FROM page_view p

JOIN user u ON(pv.userid=u.userid)

JOIN newuser x on(u.age=x.age);


如果MapReduce的任务数目和Join操作的数目是对应的,那么上述查询和以下查询是等价的:


INSERT OVERWRITE TABLE tmptable

SELECT*FROM page_view p JOIN user u

ON(pv.userid=u.userid);

INSERT OVERWRITE TABLE pv_users

SELECT x.pageid, x.age FROM tmptable x

JOIN newuser y ON(x.age=y.age);


4.Map Join操作

Map Join操作无须Reduce操作就可以在Map阶段全部完成,前提是在Map过程中可以访问到全部需要的数据。比如如下查询:


INSERT OVERWRITE TABLE pv_users

SELECT/+MAPJOIN(pv)/pv.pageid, u.age

FROM page_view pv

JOIN user u ON(pv.userid=u.userid);


这个查询便可以在Map阶段全部完成Join。此时还须设置的相关属性为:hive.join.emit.inter-l=1000、hive.mapjoin.size.key=10000、hive.map-join.cache.numrows=10000。hive.join.emit.inter-l=1000属性定义了在输出Join的结果前,还要判断右侧进行Join的操作数最多可以加载多少行到缓存中。

5.Group By操作

进行Group BY操作时需要注意以下两点。

Map端部分聚合。事实上,并不是所有的聚合操作都需要在Reduce部分进行,很多聚合操作都可以先在Map端进行部分聚合,然后在Reduce端得出最终结果。

这里需要修改的参数包括:hive.map.aggr=true,用于设定是否在Map端进行聚合,默认为True。hive.groupby.mapaggr.checkinterval=100000,用于设定在Map端进行聚合操作的条目数。

有数据倾斜(数据分布不均匀)时进行负载均衡。此处需要设定hive.groupby.skewindata,当选项为true时,生成的查询计划会有两个MapRreduce任务。在第一个MapReduce中,Map的输出结果集合会随机分布到Reduce中,对每个Reduce做部分聚合操作并输出结果。这样处理的结果是,相同的Group By Key有可能被分发到不同的Reduce中,从而达到负载均衡的目的;第二个MapReduce任务再根据预处理的数据结果按照Group By Key分布到Reduce中(这个过程可以保证相同的Group By Key分布到同一个Reduce中),最后完成最终的聚合操作。

6.合并小文件

在第9章“HDFS详解”中我们知道,文件数目过多会给HDFS带来很大的压力,并且会影响处理的效率。因此,我们可以通过合并Map和Reduce的结果文件来消除这样的影响。需要进行的设定有以下三个:hive.merge.mapfiles=true,设定是否合并Map输出文件,默认为True;hive.merge.mapredfiles=false,设定是否合并Reduce输出文件,默认为False;hive.merge.size.per.task=25610001000,设定合并文件的大小,默认值为256 000 000。