SELECT a,b FROM T WHERE e < 10;
Hive 针对不同的查询进行了优化,优化可以通过配置进行控制,本文将介绍部分优化的策略以及优化控制选项。
列裁剪(Column Pruning)
在读数据的时候,只读取查询中需要用到的列,而忽略其他列。例如,对于查询:
其中,T 包含 5 个列 (a,b,c,d,e),列 c,d 将会被忽略,只会读取a, b, e 列
这个选项默认为真: hive.optimize.cp = true
分区裁剪(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
Join
在使用写有 Join 操作的查询语句时有一条原则:应该将条目少的表/子查询放在 Join 操作符的左边。原因是在 Join 操作的 Reduce 阶段,位于 Join 操作符左边的表的内容会被加载进内存,将条目少的表放在左边,可以有效减少发生 OOM 错误的几率。
对于一条语句中有多个 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 相同,不管有多少个表,都会则会合并为一个 Map-Reduce
- 一个 Map-Reduce 任务,而不是 ‘n’ 个
- 在做 OUTER 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.age = x.age);
Map-Reduce 的任务数目和 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);
Map Join
Join 操作在 Map 阶段完成,不再需要Reduce,前提条件是需要的数据在 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.interval = 1000 How many rows in the right-most join operand Hive should buffer before emitting the join result.
- hive.mapjoin.size.key = 10000
- hive.mapjoin.cache.numrows = 10000
转载一个新的mapjion 说明:
今天遇到一个hive的问题,如下hive sql:
select f.a,f.b from A t join B f on ( f.a=t.a and f.ftime=20110802)
该语句中B表有30亿行记录,A表只有100行记录,而且B表中数据倾斜特别严重,有一个key上有15亿行记录,在运行过程中特别的慢,而且在reduece的过程中遇有内存不够而报错。
为了解决用户的这个问题,考虑使用mapjoin,mapjoin的原理:
MAPJION会把小表全部读入内存中,在map阶段直接拿另外一个表的数据和内存中表数据做匹配,由于在map是进行了join操作,省去了reduce运行的效率也会高很多
这样就不会由于数据倾斜导致某个reduce上落数据太多而失败。于是原来的sql可以通过使用hint的方式指定join时使用mapjoin。
select /*+ mapjoin(A)*/ f.a,f.b from A t join B f on ( f.a=t.a and f.ftime=20110802)
再运行发现执行的效率比以前的写法高了好多。
mapjoin还有一个很大的好处是能够进行不等连接的join操作,如果将不等条件写在where中,那么mapreduce过程中会进行笛卡尔积,运行效率特别低,如果使用mapjoin操作,在map的过程中就完成了不等值的join操作,效率会高很多。
例子:
select A.a ,A.b from A join B where A.a>B.a
简单总结一下,mapjoin的使用场景:
1. 关联操作中有一张表非常小
2.不等值的链接操作
Group By
- Map 端部分聚合:
- 并不是所有的聚合操作都需要在 Reduce 端完成,很多聚合操作都可以先在 Map 端进行部分聚合,最后在 Reduce 端得出最终结果。
- 基于 Hash
- 参数包括:
- hive.map.aggr = true 是否在 Map 端进行聚合,默认为 True
- hive.groupby.mapaggr.checkinterval = 100000 在 Map 端进行聚合操作的条目数目
- 有数据倾斜的时候进行负载均衡
- hive.groupby.skewindata = false
- 当选项设定为 true,生成的查询计划会有两个 MR Job。第一个 MR Job 中,Map 的输出结果集合会随机分布到 Reduce 中,每个 Reduce 做部分聚合操作,并输出结果,这样处理的结果是相同的 Group By Key 有可能被分发到不同的 Reduce 中,从而达到负载均衡的目的;第二个 MR Job 再根据预处理的数据结果按照 Group By Key 分布到 Reduce 中(这个过程可以保证相同的 Group By Key 被分布到同一个 Reduce 中),最后完成最终的聚合操作。
合并小文件
文件数目过多,会给 HDFS 带来压力,并且会影响处理效率,可以通过合并 Map 和 Reduce 的结果文件来消除这样的影响:
- hive.merge.mapfiles = true 是否和并 Map 输出文件,默认为 True
- hive.merge.mapredfiles = false 是否合并 Reduce 输出文件,默认为 False
- hive.merge.size.per.task = 256*1000*1000 合并文件的大小