Hive的连接(join)方案

来源:互联网 发布:.blog域名 编辑:程序博客网 时间:2024/06/06 01:49

一 Common JOIN/Reduce Side JOIN/Shuffle JOIN

这三种其实都是一种连接方案:即在Reduce端做JOIN操作。一般情况下,如果不手动指定MapJoin或者不满足MapJoin的条件,一般Hive解析器会将Join操作转换成ReduceJoin. 他会经历完整的Map

->Shuffle->Reduce三个阶段

Map阶段: 读取表中数据,输出的时候以JoinKey作为OutputKey,如果有多个关联条件,然后这些会组合成一个key, 输出的时候以SELECT字段或者WHERE条件需要的列作为OutputValue,同时还会包含表的tag信息,以此标志这个value属于哪一个表

Shuffle阶段:根据key进行partition操作并且根据key进行排序,可以确保相同的key在同一个Reduce之中

Reduce阶段:根据key值完成JOIN操作,通过不同的tag来识别不同表中的数据

 

我们考虑个问题:在Reduce端做JOIN,肯定各个Reduce端要去对应Map端取数据,其中的shuffle过程想必都是知道的,所以如果表态大,会有大量的磁盘I/O和网络I/O操作,所以这种情况下,如果是大表之间JOIN是不太适合这种方案的。

适合场景:

两张表大小相当,而且表不是很大的适合可以使用这种方式,顺便集合一下Hive中Reduce的调优是没有问题的:

#比如开启中间压缩和最终输出压缩:但是CPU会增加,但是HDFS 写的时间会减少

sethive.exec.compress.intermediate=true;

setmapred.map.output.compression.codec=org.apache.hadoop.io.

compress.SnappyCodec

sethive.exec.compress.output=true;

setmapred.output.compression.codec=org.apache.hadoop.io.compress.

GZipCodec   

#设置set mapred.max.split.size调大一点:CPU可以适当的减少

 

二 MapSide Join/Broadcast JOIN

如果有一张小表和大表做JOIN,那么这个时候,我们使用Reduce 端做JOIN是不太划算的,因为我们完全可以避免去跑Reduce。

在Map端直接读入小表进内存,然后在和大表JOIN,JOIN完毕,数据直接写入HDFS,完全没有必要去走Reduce

首先我们需要确定什么小表,它是由什么决定的呢?它是由hive.mapjoin.smalltable.filesize来决定,默认是hive.mapjoin.smalltable.

filesize=25000000,大约23M左右。

在以前如果需要使用MapJoin只能通过Hint提示/*+mapjoin(表名)*/

比如这种:

SELECT/*+MAPJOIN(time_dim)*/ count(*) FROM store_sales

sJOIN time_dim t ON s.ss_old_time_sk = t.t_tim_sk;

才能使用MapJoin,现在已经有参数可以控制是否自动转换成MapJoin,这个转换有参数:hive.auto.convert.join=true来控制,默认就是true.

 

原理:


1客户端本地执行的task1,它会扫描小表的的数据,然后将其转换成hashtable的数据结构,并写入本地的文件中,

/tmp/hadoop/479fb43b-6fc4-4aa8-846c-a052944ad02d/hive_2015-03-18_15-18-13_374_8641342742824793758-1/-local-10003/HashTable-Stage-3/MapJoin-mapfile01--.hashtable

2之后将该文件加载到DistributedCache中,

Uploaded1 File to:file:/tmp/hadoop/479fb43b-6fc4-4aa8-846c-a052944ad02d/hive_2017-03-18_15-18-13_374_8641342742824793758-1/-local-10003/HashTable-Stage-3/MapJoin-mapfile01--.hashtable

 

3Task 2 是一个没有Reduce的task,启动之后扫描大表,在map阶段,根据大表每一条记录然后去和DistributedCache中对应hashtable关联,然后输出结果,所以有多少个maptask就有多少个文件

 

 

三 SMBJOIN (Sort-Merge-Bucket)

我们试想一个场景:2张大表参与JOIN, 都是上千万或者上亿条记录,

假设这时候,这两张表中小表可能数据太多,不太适合加载进内存,那么MapJoin肯定是不合适的,那么ReduceJOIN呢?假设A表有数据4000万条,B表有数据6000万条,这时候ReduceJOIN首先肯定大量网络I/O和磁盘I/O,另外A表数据需要逐条和B表数据进行匹配吧,这个效率肯定不行。所以SMB JOIN可以带来性能很大的提升

首先: 两张表都是可以分桶的,在创建表的时候需要指定:

CREATETABLE(……) CLUSTERED BY (col_name) SORTED BY

(col_name)INTO bucketsNum BUCKETS

其次:两张表分桶的列必须是JOIN KEY

最后:需要设置一些bucket相关的参数

在导入数据值前,我们需要设置hive.enfoce.bucketing=true,如果我们没有设置,我们就需要设置与桶数匹配的reducer数目,并在查询的时候需要添加CLUSTER BY子句

 

然后,我们希望SMB JOIN转换成SMB Map JOIN需要设置一下参数:

sethive.auto.convert.sortmerge.join=true; 

set hive.optimize.bucketmapjoin = true; 

set hive.optimize.bucketmapjoin.sortedmerge =true;    

 

原理:

在Map JOIN的时候,两个表是以相同的方式划分通的,则处理左边表的某个桶的时候,Mapper是知道表内对应的记录也在右边表的相同的桶内。因此Mapper只需要获取那个桶,然后进行连接就可以,就不用去每一条数据都去匹配一次。

桶中的数据可以根据一个列或者多个列排序,这样每个桶的JOIN就变成了MERGE SORT,可以进一步提升Map JOIN的效率

四 Skew Join

真实数据中数据倾斜是一定的, hadoop 中默认是使用

hive.exec.reducers.bytes.per.reducer =1000000000

也就是每个节点的reduce 默认是处理1G大小的数据,如果你的join 操作也产生了数据倾斜,那么你可以在hive 中设定

set hive.optimize.skewjoin = true; 
set hive.skewjoin.key = skew_key_threshold (default = 100000)

hive 在运行的时候没有办法判断哪个key 会产生多大的倾斜,所以使用这个参数控制倾斜的阈值,如果超过这个值,新的值会发送给那些还没有达到的reduce, 一般可以设置成你

(处理的总记录数/reduce个数)的2-4倍都可以接受.

倾斜是经常会存在的,一般select 的层数超过2层,翻译成执行计划多于3个以上的mapreduce job 都很容易产生倾斜,建议每次运行比较复杂的sql 之前都可以设一下这个参数. 如果你不知道设置多少,可以就按官方默认的1个reduce 只处理1G 的算法,那么  skew_key_threshold  =1G/平均行长. 或者默认直接设成250000000(差不多算平均行长4个字节)