一 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个字节)