Hive中的Join可分为Common Join(Reduce阶段完成join)和Map Join(Map阶段完成join)。简单介绍一下两种join的原理和机制。
Common Join
Map阶段
读取源表的数据,Map输出时候以Join on条件中的列为key,如果Join有多个关联键,则以这些关联键的组合作为key;
Map输出的value为join之后所关心的(select或者where中需要用到的)列;同时在value中还会包含表的Tag信息,用于标明此value对应哪个表;
按照key进行排序
Shuffle阶段
根据key的值进行hash,并将key/value按照hash值推送至不同的reduce中,这样确保两个表中相同的key位于同一个reduce中
Reduce阶段
根据key的值完成join操作,期间通过Tag来识别不同表中的数据。
Map Join
由于hive在进行join的过程中要进行shuffle
如果两个表中有一个表比较小,将这个小表加入到每个mapper的内存中, 就可以省去shuffle的过程
--开启默认
set hive.auto.convert.join=true;
--小于这个参数大小的小表则会变成MapJoin为(默认2500000Byte)
set hive.mapjoin.smalltable.filesize=2500000;
如果依次关联n个小表,一般会执行n个MapJoin的过程, 可以通过设置参数将这n个MapJoin合并成为一个
--开启合并MapJoin
set hive.auto.convert.join.noconditionaltask=true;
--n个小表大小的合计, 小于这个合计就可以进行合并(默认1000000Byte)
set hive.auto.convert.join.noconditionaltask.size:
Hive0.7之前,需要使用hint提示 /+ mapjoin(table) /才会执行MapJoin,否则执行Common Join,但在0.7版本之后,默认自动会转换Map Join,由参数hive.auto.convert.join来控制,默认为true.
如图中的流程,首先是Task A,它是一个Local Task(在客户端本地执行的Task),负责扫描小表b的数据,将其转换成一个HashTable的数据结构,并写入本地的文件中,之后将该文件加载到DistributeCache中,该HashTable的数据结构可以抽象为:
key | value |
1 | 12312 |
2 | 12344 |
接下来是Task B,该任务是一个没有Reduce的MR,启动MapTasks扫描大表a,在Map阶段,根据a的每一条记录去和DistributeCache中b表对应的HashTable关联,并直接输出结果。
由于MapJoin没有Reduce,所以由Map直接输出结果文件,有多少个Map Task,就有多少个结果文件。
总的来说,因为小表的存在,可以在Map阶段直接完成Join的操作,为了优化小表的查找速度,将其转化为HashTable的结构,并加载进分布式缓存中。
总结
Common Join 和 Map Join 是以mr为底层在执行hive语句的情况下根据配置自动触发的流程,所以一般无需修改。而且虽然说mapjoin会比reduce少了shuffle,但结果文件的急剧增多也会导致nn读取数据的慢(也就是小文件问题),所以要根据情况是使用,临时中间表比较合适。