一、案例演示
1)没有使用map join
-- a 表是大表,数据量是百万级别
-- b 表是小表,数据量在百级别
select
a.field1 as field1,
b.field2 as field2,
b.field3 as field3
from a left join b
on a.field1 = b.field1;
-- a 表是大表,数据量是百万级别
-- b 表是小表,数据量在百级别
select
a.field1 as field1,
b.field2 as field2,
b.field3 as field3
from a left join b
on a.field1 = b.field1;
运行时间为:总耗时:08:10:03
2) 使用map join 优化后
-- a 表是大表,数据量是百万级别
-- b 表是小表,数据量在百级别
-- 特别说明:mapjion括号中的b就是指定哪张表为小表
select
/*+mapjoin(b)*/
a.field1 as field1,
b.field2 as field2,
b.field3 as field3
from a left join b
on a.field1 = b.field1;
-- a 表是大表,数据量是百万级别
-- b 表是小表,数据量在百级别
-- 特别说明:mapjion括号中的b就是指定哪张表为小表
select
/*+mapjoin(b)*/
a.field1 as field1,
b.field2 as field2,
b.field3 as field3
from a left join b
on a.field1 = b.field1;
总耗时:00:12:39
通过对比,map join大大提升了SQL执行效率,节省分析时间
二、map join是如何解决数据小表与大表join优化?
背景说明:A表有百万级、千万级、亿级,B表2万行及2万以内的记录,而且A表中数据倾斜特别严重,有一个key上有30%或40%以上记录,在运行中特别的慢,而且在reduce的过程中遇到内存不够而报错。
这个问题属于典型的小表与join优化问题,考虑使用mapjoin 进行优化
mapjoin的原理: MapJoin 会把小表全部读入内存中,在map阶段直接拿另外一个表的数据和内存中表数据做匹配,由于在map是进行了join操作,省去了reduce 阶段,运行的效率就会高很多。
为了更方便理解,mapjoin原理大白话:小表复制到各个节点上,并加载到内存中;大表分片,与小表完成连接操作。
这样就不会由于数据倾斜导致某个reduce上落的数据太多而失败。于是原来的sql 可以通过使用hint 的方式指定join 时使用 mapjoin,示例如下:
select
/*+mapjoin(small_table)*/
big_table.a, small_table.b
from big_table left join small_table
on big_table.a = small_table.a;
select
/*+mapjoin(small_table)*/
big_table.a, small_table.b
from big_table left join small_table
on big_table.a = small_table.a;
添加了hint 显示明确将small_table 读入内存中,通过此方式运行的话效率比以前的写法高了很多。
那么问题来了,什么样的表应该被视为小表small_table?
2万条为宜,大小不超过25M.
三、map join原理
Hive 整个过程分为Map、Shuffle、Reduce三大阶段。
简单的说,Hive中的Join 可分为Common Join(Reduce阶段完成join)和 Map Join (Map阶段完成join)。
A、Map阶段
读取源表的数据,Map输出时候以Join on条件中的列为key,如果Join有多个关联键,则以这些关联键的组合作为key;Map输出的value 为join之后所关心的(select 或者where 中需要用到的)列;同时在value中还会包含表的Tag信息,用于标明此value对应哪个表;按照key进行排序
B、Shuffle阶段
根据key的值进行hash,并将key/value按照hash值推送至不同的reduce中,这样确保两个表中相同的key位于同一个reduce中
C、Reduce阶段
根据key的值完成join操作,期间通过Tag来识别不同表中的数据。
1)Hive Common Join
如果不指定Map Join或者不符合MapJoin的条件,那么Hive解析器会将Join操作转换成Common Join,即:在Reduce阶段完成join. 即:需要通过map、shuffle、reduce三个大的阶段
2)Hive Map Join
2.1 什么是Map Join?
MapJoin 顾名思义,就是在Map阶段进行表之间的连接。而不需要进入到Reduce阶段才进行连接。这样就节省了在Shuffle阶段时要进行的大量数据传输。从而起到了优化作业的作用。
2.2 MapJoin的原理:
通过情况下,要连接的各个表里面的数据会分布在不同的Map中进行处理。即同一个Key对应的Value可能存在不同的Map中。这样就必段等到Reduce中去连接。要使MapJoin能够顺利进行,那就必段满足这样的条件:除了一份表的数据分布在不同的Map中外,其他连接的表的数据必须在每个Map中有完整的拷贝。
2.3 MapJoin适用的场景:
通过上面分析你会发现,并不是所有的场景都适合用MapJoin。它通常会用在如下的一些情景:在二个要连接的表中,有一个很大,有一个很小,这个小表可以存放在内存中而不影响性能。这样我们就把小表文件复制到每一个Map任务的本地,再让Map把文件读取内存中待用。
2.4 MapJoin的实现方法:
1)在Map-Reduce的驱动程序中使用静态方法,DistributedCache.addCacheFile() 增加要拷贝的小表文件。JobTracker在作业启动之前会获取这个URI列表,并将相应的文件拷贝到各个TaskTracker的本地磁盘上。
2)在Map类的setup方法中使用DistributedCache.getLocalCacheFiles() 方法获取文件目录,并使用标准的文件读写API读取相应的文件。
2.5 Hive 内置提供的优化机制之一就包括MapJoin
/*+mapjoin(tableNameA,tableNameB,...)*/
B、Hive v0.7之后的版本已经不需要给出MapJoin的指示就进行优化。它是通过如下配置参数来控制的:
hive> set hive.auto.convert.join=true;
Hive 还提供另外一个参数,就是:表文件的大小作为开启和关闭MapJoin的阈值。
hive> set hive.mapjoin.smalltable.filesize=25000000 即25M