案例一:
select
a.id,a.number,b.number,c.number
from table_tmp a
join table_tmp b on a.id = b.id
join table_tmp c on a.id = c.id
where a.business = 'A'
and b.business = 'B'
and c.business = 'C'
如上例中,Hive会对每对join连接对象启动一个MaoReduce任务。首先启动一个MapReduce job对表a和表b进行连接操作,然后再启动一个MapReduce job将第一个MapReudce job的输出和表c进行连接操作。
这里可能有的同学就提出疑问了?为什么不是表b和表c先进行连接操作呢?
因为Hive总是按照从左到右的顺序执行的。
补充:前边的例子中,每个on子句都用到了a.id作为其中一个join连接键。这种情况下,Hive中有个优化:当对3个表或者更多个表进行join连接时,如果每个on子句都使用相同的连接键的话,那么只会产生一个MapReduce job。
案例二(JOIN优化):
select
a.id,a.number,b.number
from big_table_tmp a
join small_table_tmp b on a.id = b.id
表a为大表,表b为小表,我们错误的将小表,放在了后边。
优化:
应该交换下表a和表b的位置,如下:
select
a.id,a.number,b.number
from small_table_tmp a
join big_table_tmp b on a.id = b.id
这样在查询中最后一个表为最大的那个表,在对每行记录进行操作时,它会尝试将其他表缓存起来,然后扫描最后那个表进行计算。因此,我们在编写SQL时需保证查询中表的大小从左到右是依次增加的。同时Hive也提供了一个“标记”机制来显式的告知查询优化器哪张表是大表,使用如下:
select /* STREAMTABLE(a)*/
a.id,a.number,b.number
from big_table_tmp a
join small_table_tmp b on a.id = b.id
这里,Hive通过标记,会将表a 作为驱动表,即使其在查询中不是位于最后面的。
案例三(map-side JOIN):?
?如果所有表中只有一张表是小表,那么可以在最大的表通过mapper的时候将小表完全放到内存中。Hive可以在map端执行连接的过程我们称之为map-side JOIN。这是因为Hive可以和内存中的小表进行逐一匹配,从而省略掉常规连接操作所需要的reduce过程。即使对于很小的数据集,这个优化也明显地要快于常规的连接操作,这样不仅减少了reduce过程,而且有时还可以同时减少map过程的执行步骤。
select /* +MAPJOIN(b)*/
a.id,a.number,b.number
from big_table_tmp a
join small_table_tmp b on a.id = b.id
注意:从Hive v0.7开始,废弃了这种标记的方式,不过如果增加这个标记也是有效的。如果不增加这个标记,用户需要设置属性 hive.auto.convert.join=true,这样Hive才会在必要的时候启动这个优化,默认情况下为false。
set hive.auto.convert.join=true;
select
a.id,a.number,b.number
from big_table_tmp a
join small_table_tmp b on a.id = b.id
?同时需要注意的是,用户也可以配置能够使用这个优化的小表的大小,默认值如下:
hive.mapjoin.smalltable.filesize=25000000
如果用户期望Hive在必要的时候自动启动这个优化的话,也可以将这个(或其他优化配置)设置在${HIVE_HOME}/bin目录下有个.hiverc文件中。
|