Map Join
是Hive的一种优化操作,其适用于小表JOIN
大表的场景,由于表的JOIN
操作是在Map
端且在内存进行的,所以其并不需要启动Reduce
任务也就不需要经过shuffle
阶段,从而能在一定程度上节省资源提高JOIN
效率
一、Map Join原理
MAPJION
会把小表全部读入内存中,在map
阶段直接拿另外一个表的数据和内存中表数据做匹配,而普通的equality join
则是类似于mapreduce
模型中的file join
,需要先分组,然后再reduce
端进行连接,使用的时候需要结合着场景;由于mapjoin
是在map
是进行了join
操作,省去了reduce
的运行,效率也会高很多。这样就不会由于数据倾斜导致某个reduce
上落数据太多而失败。于是原来的sql
可以通过使用hint
的方式指定join
时使用mapjoin
。
Hive Map Join
MapJoin
通常用于一个很小的表和一个大表进行join
的场景,具体小表有多小,由参数hive.mapjoin.smalltable.filesize来决定
,该参数表示小表的总大小,默认值为25000000字节,即25M
。
Hive0.7
之前,需要使用hint
提示/*+ mapjoin(table) */
才会执行MapJoin
,否则执行Common Join
,但在0.7版本之后,默认自动会转换Map Join
,由参数hive.auto.convert.join
来控制,默认为true
.
仍然以9.1中的HQL
来说吧,假设a表为一张大表,b为小表,并且hive.auto.convert.join=true
,那么Hive在执行时候会自动转化为MapJoin
。
Map Join基本架构:
如图中的流程,首先是Task A
,它是一个Local Task
(在客户端本地执行的Task
),负责扫描小表b的数据,将其转换成一个HashTable
的数据结构,并写入本地的文件中,之后将该文件加载到DistributeCache
中,该HashTable
的数据结构可以抽象为:
key | value |
---|---|
1 | 26 |
2 | 34 |
图中红框圈出了执行Local Task
的信息。
接下来是Task B
,该任务是一个没有Reduce
的MR
,启动MapTasks
扫描大表a,在Map
阶段,根据a的每一条记录去和DistributeCache
中b表对应的HashTable
关联,并直接输出结果。
由于MapJoin
没有Reduce
,所以由Map
直接输出结果文件,有多少个Map Task
,就有多少个结果文件。
二、Map Join的使用场景:
- 关联操作中有一张表非常小
- 不等值的链接操作
mapjoin
还有一个很大的好处是能够进行不等连接的join
操作,如果将不等条件写在where
中,那么mapreduce
过程中会进行笛卡尔积,运行效率特别低,这是由于equality join
(不等值join
操作有>、<、like
等如:a.x < b.y
或者a.x like b.y
) 需要在reduce
端进行不等值判断,map
端只能过滤掉where
中等值连接时候的条件,如果使用mapjoin
操作,在map
的过程中就完成了不等值的join
操作,效率会高很多。
例子:
select
A.a,
A.b
from A
join B
where A.a>B.a
三、Map Join使用方法:
方法一:
在Hive0.11
前,必须使用MAPJOIN
来标记显示地启动该优化操作,由于其需要将小表加载进内存所以要注意小表的大小
SELECT/*+MAPJOIN(smalltable)*/
big.key AS key,
big.value AS value
FROM smalltable small
JOIN bigtable big
ON small.key=big.key
方法二:
在Hive0.11
后,Hive
默认启动该优化,也就是不在需要显示的使用MAPJOIN
标记,其会在必要的时候触发该优化操作将普通JOIN
转换成MapJoin
,可以通过以下两个属性来设置该优化的触发时机
hive.auto.convert.join
默认值为true
,自动开户MAPJOIN
优化
hive.mapjoin.smalltable.filesize
默认值为2500000(25M)
,通过配置该属性来确定使用该优化的表的大小,如果表的大小小于此值就会被加载进内存中
注意:使用默认启动该优化的方式如果出现默名奇妙的BUG(比如MAPJOIN并不起作用),就将以下两个属性置为fase手动使用MAPJOIN标记来启动该优化
hive.auto.convert.join=false(关闭自动MAPJOIN转换操作)
hive.ignore.mapjoin.hint=false(不忽略MAPJOIN标记)
对于以下查询是不支持使用方法二(MAPJOIN标记)来启动该优化的
SELECT/*+MAPJOIN(smallTableTwo)*/
idOne,
idTwo,
value
FROM
(SELECT/*+MAPJOIN(smallTableOne)*/
idOne,
idTwo,
value
FROM bigTable
JOIN smallTableOne
on (bigTable.idOne= smallTableOne.idOne)
) firstjoin
JOIN smallTableTwo
ON(firstjoin.idTwo=smallTableTwo.idTwo)
但是,如果使用的是方法一即没有MAPJOIN标记则以上查询语句将会被作为两个MJ执行,进一步的,如果预先知道表大小是能够被加载进内存的,则可以通过以下属性来将两个MJ合并成一个MJ
hive.auto.convert.join.noconditionaltask:Hive在基于输入文件大小的前提下将普通JOIN转换成MapJoin,并是否将多个MJ合并成一个
hive.auto.convert.join.noconditionaltask.size:多个MJ合并成一个MJ时,其表的总的大小须小于该值,同时hive.auto.convert.join.noconditionaltask必须为true
四、具体案例
正例一:
遇到一个hive的问题,如下hive sql:
select t1.a,
t1.b
from table t1
join table2 t2
on t1.a=t2.a
and t1.datecol=20110802
该语句中B表有30亿行记录,t1表只有100行记录,而且t2表中数据倾斜特别严重,有一个key上有15亿行记录,在运行过程中特别的慢,而且在reduece的过程中遇有内存不够而报错。
在sql开启mapjoin
后如下:
select /*+ mapjoin(t1)*/
t1.a,
t1.b
from table t1
join table2 t2
on t1.a=t2.a
and f.ftime=20110802
再运行发现执行的效率比以前的写法高了好多,时间由80分钟优化至10分钟内。
五、使用说明
- 使用
MAPJOIN
时,在引用小表或子查询时,需要引用别名。 MAPJOIN
支持小表为子查询。LEFT OUTER JOIN
的左表必须是大表。RIGHT OUTER JOIN
的右表必须是大表。INNER JOIN
的左表或右表均可以作为大表。FULL OUTER JOIN
不能使用MAPJOIN。- 在
MAPJOIN
中,可以使用不等值连接或者OR连接多个条件。您可以通过不写ON
语句而通过MAPJOIN ON 1 = 1
的形式,实现笛卡尔乘积的计算,例如SELECT /* + MAPJOIN(a) */ a.id FROM shop a JOIN table_name b ON 1=1
,但此操作可能带来数据量膨胀问题。 - 在
MAPJOIN
中最多支持指定128张小表,否则报语法错误。MAPJOIN
中多个小表用逗号隔开,例如/*+MAPJOIN(a,b,c)*/
。 - 如果使用
MAPJOIN
,则小表占用的总内存不得超过512 MB
。由于hdfs
文件采用的是压缩存储,因此小表在被加载到内存后,数据大小会急剧膨胀。此处的512 MB
是指加载到内存后的空间大小。
六、拓展:Hive Sql Common Join
Hive
中的Join
可分为Common Join
(Reduce
阶段完成join
)和Map Join
(Map
阶段完成join
)。本文简单介绍一下两种join
的原理和机制。
Hive Common Join
如果不指定MapJoin
或者不符合MapJoin
的条件,那么Hive
解析器会将Join
操作转换成Common Join
,即:在Reduce
阶段完成join
.整个过程包含Map、Shuffle、Reduce
阶段。
Hive 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
来识别不同表中的数据。
以下面的HQL为例,图解其过程:
SELECT
a.id,
a.dept,
b.age
FROM a join b
ON (a.id = b.id);
看了这个图,应该知道如何使用MapReduce
进行join
操作了吧。
参考:[一起学Hive]之十-Hive中Join的原理和机制