Hive的执行引擎默认是MapReduce,所以hive join实际是MapReduce join。当然如果是hive on spark模式,那么hive join就是spark join。
Hive join(MapReduce)
mr的join可以分成 common join(reduce阶段完成join)和map join(map阶段完成join)。
1.common join
如果不指定map join,或者不满足map join的条件,那么hive解析器默认进行common join操作,即在reduce阶段完成join。整个过程分为map,shuffle和reduce阶段。
- map阶段:
读取源表中的数据, 以 join on中的条件列作为key(如果有多个,可以进行组合),select,where中的列作为value,value中还会加上tag,标记这条数据属于哪个表。 - shuffle阶段:
对key进行hash,按照hash值送到不同的reduce中,相同hash值的数据送到相同的reduce中。 - reduce阶段:
reduce阶段完成join操作。reduce内部根据tag来判断数据是来自1表还是2表。在内部分成两组,做笛卡尔积。
实例如下:
SELECT a.id,a.dept,b.age FROM a join b ON (a.id = b.id)
2.map join
map join通常用于一个很小的表和一个大表join的场景,小表的大小由参数hive.mapjoin.smalltable.filesize来决定,默认25M。令hive.auto.convert.join=true,hive会在小表满足条件的时候自动转换为map join。
map端进行join的原理是broadcast join,把小表作为一个完成的驱动表进行join。通常情况下,一个表中的数据会分布在不同的map中进行处理,相同一个key对应的value可能存在不同的map中,这样就必须等到reduce中去连接。map join把小表全部读到内存中,发送到大表所在节点的内存中,在map阶段直接拿另外一个表的数据和内存中的表的数据做匹配。由于在map中进行了join操作,省去了reduce,运行的效率会高很多。
执行流程 :
(1)首先是Task A,它是一个Local Task(在客户端本地执行的task),负责扫描小表b的数据,将其转换成一个hashtable的数据结构,并写如到本地的文件中,之后将改文件加载到DistributeCache中。
(2)接下来是Task B,该任务是一个没有reduce的mr,启动map tasks扫描大表a,在map阶段,根据a的每一条记录去和DistributeCache中b表对应的HashTable关联,并直接输出结果。
(3)相关参数:
- 小表自动选择Mapjoin:set hive.auto.convert.join=true;默认值:false。该参数为true时,Hive自动对左边的表统计量,若是小表就加入内存,即对小表使用Map join
- 小表阀值:set hive.mapjoin.smalltable.filesize=25M;
Spark join
Spark将参与join的两张表抽象为流式遍历表(streamIter)和查找表(buildIter)。通常streamIter是大表,buildIter是小表,Spark根据join语句自动区分大小表。
1.broadcast join --小表对大表
将小表的数据分发到每个节点上,供大表使用。executor存储小表的全部数据,一定程度上牺牲了空间,换取shuffle操作大量的耗时。broadcast join的条件是:
- (1)被广播的表需要小于 spark.sql.autoBroadcastJoinThreshold 所配置的值,默认是10M (或者加了broadcast join的hint)
- (2)基表不能被广播,比如 left outer join 时,只能广播右表
2.Shuffle Hash Join
因为被广播的表首先被collect到driver端,然后被冗余分发到每个executor上,所以当表较大时,对driver和executor端造成的压力比较大。
spark可以通过分区的形式将大批量的数据划分成n份较小的数据集进行并行计算。利用key相同必然分区相同的原理,sparkSQL将较大的表分而治之,先将表划分成n个分区,在对两个表中对应分区的数据分别进行hash join。
步骤:
- 对两张表分别按照join keys进行重分区,即shuffle,目的是为了让有相同join keys的值分到对应的分区中。
- 对对应分区中的数据进行join,此处先将小表分区构造成一张hash表,然后根据大表分区中记录的join keys,拿出来进行匹配。
条件是:
- 分区的平均大小不超过spark.sql.autoBroadcastJoinThreshold所配置的值,默认是10M
- 基表不能被广播,比如left outer join时,只能广播右表
- 一侧的表要明显小于另外一侧,小的一侧将被广播(明显小于的定义为3倍小,此处为经验值)
3.Sort Merge Join --大表对大表
将两张表按照join keys进行了重新shuffle,保证join keys值相同的记录会被分在相应的分区。分区后对每个分区内的数据进行排序,排序后再对相应的分区内的记录进行连接
因为两个序列都是有序的,从头遍历,碰到key相同的就输出;如果不同,左边小就继续取左边,反之取右边(即用即取即丢)