如何优化Hive Join:提升大数据查询效率的实战技巧
1.1 JOIN 优化
JOIN 是数据分析处理过程中必不可少的操作,Hive 同样支持 JOIN 的语法。而 Hive JOIN 的底层是通过 MapReduce 来
实现的,Hive 实现 JOIN 时,为了提高 MapReduce 的性能,提供了多种 JOIN 方案:
-
小表 JOIN 大表的 Map Join
-
大表 JOIN 大表的 Reduce Join,Reduce Join 又分为以下两种:
-
Bucket Map Join(中型表和大表 JOIN)
-
Sort Merge Bucket Join(大表和大表 JOIN)
-
2.2 Map Join
Map Join 顾名思义,就是在 Map 阶段进行表之间的连接。而不需要进入到 Reduce 阶段才进行连接。这样就节省了
在 Shuffle 阶段时要进行的大量数据传输。从而起到了优化作业的作用。
Map Join 简单说就是在 Map 阶段将小表读入内存,顺序扫描大表完成 Join。

上图是 Hive MapJoin 的原理图,出自 Facebook 工程师 Liyin Tang 的一篇介绍 Join 优化的文章,从图中可以看出
MapJoin 分为两个阶段:
-
通过 MapReduce Local Task 将小表读入内存,生成 HashTableFiles 上传至 Distributed Cache 中,这里会对HashTableFiles 进行压缩。
-
MapReduce Job 在 Map 阶段,每个 Mapper 从 Distributed Cache 读取 HashTableFiles 到内存中,顺序扫描大表,在
Map 阶段直接进行 Join,将数据传递给下一个 MapReduce 任务。
应用场景:小表 JOIN 大表或者小表 JOIN 小表。
实现原理:Map Join 会把小表全部读入内存中,在 Map 阶段直接拿另外一个表的数据和内存中的表数据做匹配,由
于在 Map 阶段进行了 JOIN 操作,底层不需要经过 Shuffle,这样就不会由于数据倾斜导致某个 Reduce 上落数据太多而失
败,但是需要占用内存空间存放小表数据。
具体使用:尽量使用 Map Join 来实现 JOIN 过程,Hive 中默认开启了 Map Join:
-- 是否开启 Map Join,默认为 true
SET hive.auto.convert.join=true;
-- 2.0 版本之前的控制参数
-- 如果打开上面这个参数,当参与 Join 的表小于下面参数指定的值,将转换为 Map Join,默认为 25000000
SET hive.mapjoin.smalltable.filesize=25000000;
-- 2.0 版本开始由以下参数控制
-- 是否启用基于输入文件的大小,将 Reduce Join 转化为 Map Join,默认为 true
SET hive.auto.convert.join.noconditionaltask=true;
-- 假设参与 Join 的表(或分区)有 N 个,如果打开上面这个参数,并且有 N-1 个表(或分区)的大小总和小于下面参数指定
的值,那么会直接将 Join 转为 Map Join,默认为 10000000
SET hive.auto.convert.join.noconditionaltask.size=10000000;
-- 注意:量变引起质变,在量少的情况下是优化的操作,等到量大时没准就会出问题,所以性能优化不是一成不变的,需要具
体情况具体分析。例如好友推荐案例的分析结果。
使用优化的 Map Join 过程中没有 Shuffle 是通过本地的一个 HashTable 较小的表(较小的表的识别可以通过元数据信
息判断)生成 HashTable Files 文件,并保存到 HDFS 的临时缓存当中,然后通过与 Map 出来的另一个表进行直接匹配,得
出结果,因此过程中没有 Shuffle,不需要网络,所以效率相对来说较快,即为优化。例如:
EXPLAIN EXTENDED SELECT E.EMPNO,E.SAL,D.DEPTNO FROM EMP E JOIN DEPT D ON E.DEPTNO = D.DEPTNO;
-- 在执行计划中可以看到关于 HashTable 的操作
HashTable Sink Operator
3.3 Reduce Join
Map 端的主要工作:为来自不同表或文件的 key/value 对,打标签以区别不同来源的记录。然后用连接字段作为
Key,其余部分和新加的标志作为 Value,最后进行输出。
Reduce 端的主要工作:在 Reduce 端以连接字段作为 Key 的分组已经完成,我们只需要在每一个分组当中将那些来源
于不同文件的记录进行合并即可

应用场景:大表 JOIN 大表。
实现原理:将两张表的数据在 Shuffle 阶段利用 Shuffle 的分组将数据按照关联字段进行合并。
具体使用:Hive 会自动判断是否满足 Map Join,如果不满足 Map Join,则会自动执行 Reduce Join。
3.3.1 Bucket Map Join
大表对小表应该使用 Map Join 来进行优化,但是如果是大表对大表,如果进行 Shuffle,那就非常可怕,第一个慢不
用说,第二个容易出异常。此时就可以使用 Bucket Join 来进行优化,而 Bucket Join 又分为:
- Bucket Map Join
- Sort Merge Bucket Join(SMB Join)
具体使用流程如下:
-
将两张大表的数据构建分桶
-
数据按照分桶的规则拆分到不同的文件中
-
分桶规则 = MapReduce 分区的规则 = Key 的 Hash 取余
-
Key = 分桶的字段
-
-
只需要实现桶与桶的 JOIN 即可,减少了比较次数
分桶本质:底层 MapReduce 的分区,桶的个数 = Reduce 个数 = 文件个数

两张表 JOIN 的时候,小表不足以放到内存中,但是又想用 Map Join,这个时候就要用到 Bucket Map Join。其方法是
两个 JOIN 表都在 Join Key上都做 Hash Bucket,并且把你打算复制的那个(相对)小表的 Bucket 数设置为大表的倍数。这
样数据就会按照 Key Join 做 Hash Bucket。小表依然复制到所有节点,Map Join 的时候,小表的每一组 Bucket 加载成
HashTable,与对应的一个大表 Bucket 做局部 JOIN,这样每次只需要加载部分 HashTable 就可以了。
Map Join 条件:
- SET hive.optimize.bucketmapjoin=true; ,默认为 false
- 所有要 JOIN 的表必须分桶,如果表不是 Bucket 的,则只是做普通 JOIN
- 大表的 Bucket 数是小表的 Bucket 数的整数倍(或相等)
- Bucket 列 == JOIN 列
- 必须是应用在 Map Join 的场景中
3.4 SMB Join

SMB Join 是基于 Bucket Map Join 的有序 Bucket,可实现在 Map 端完成 JOIN 操作,只要桶内的下一条不是,就不用
再比较了,有效地减少或避免 Shuffle 的数据量。
SMB Join 的条件和 Map Join 类似但又不同:
SET hive.optimize.bucketmapjoin=true;
SET hive.auto.convert.sortmerge.join=true;
SET hive.optimize.bucketmapjoin.sortedmerge=true;
SET hive.auto.convert.sortmerge.join.noconditionaltask=true;
- 所有要 JOIN 的表必须分桶,如果表不是 Bucket 的,则只是做普通 JOIN
- 大表的 Bucket 数 = 大表的 Bucket 数
- Bucket 列 == JOIN 列 == SORT 列
ve.auto.convert.sortmerge.join.noconditionaltask=true;
- 所有要 JOIN 的表必须分桶,如果表不是 Bucket 的,则只是做普通 JOIN
- 大表的 Bucket 数 = 大表的 Bucket 数
- Bucket 列 == JOIN 列 == SORT 列
- 必须是应用在 Bucket Map Join 的场景中

983

被折叠的 条评论
为什么被折叠?



