Hint,意为提示。
1 Hint简介
本小节参考自文章Oracle hint详解中的一部分,感谢原作者的分享。
Hive的底层计算引擎MapReduce,分Mapper阶段和Reducer阶段。若有极小的表(<25M)参与join,可使用MapJoin Hint关键字在Mapper阶段即进行连接,进而避免reducer操作。
一般情况不建议使用MapJoin 关键字,因为主观认为的小表,可能突然变成超大的表,进而成为灾难。
所以,Hive0.7之前,需要使用hint提示 /*+ mapjoin(table) */才会执行MapJoin,否则执行Common Join,但在0.7版本之后,默认自动会转换Map Join,由参数hive.auto.convert.join来控制,默认为true.
假设a表为一张大表,b为小表,并且hive.auto.convert.join=true,那么Hive在执行时候会自动转化为MapJoin。
1.1 为什么引入Hint?
Hint是Oracle数据库中很有特色的一个功能,是很多DBA优化中经常采用的一个手段。那为什么Oracle会考虑引入优化器呢?基于代价的优化器是很聪明的,在绝大多数情况下它会选择正确的优化器,减轻DBA的负担。
但有时它也聪明反被聪明误,选择了很差的执行计划,使某个语句的执行变得奇慢无比。此时就需要DBA进行人为的干预,告诉优化器使用指定的存取路径或连接类型生成执行计划,从而使语句高效地运行。Hint就是Oracle提供的一种机制,用来告诉优化器按照告诉它的方式生成执行计划。
1.2 不要过分依赖Hint
当遇到SQL执行计划不好的情况,应优先考虑统计信息等问题,而不是直接加Hint了事。如果统计信息无误,应该考虑物理结构是否合理,即没有合适的索引。只有在最后仍然不能SQL按优化的执行计划执行时,才考虑Hint。
毕竟使用Hint,需要应用系统修改代码,Hint只能解决一条SQL的问题,并且由于数据分布的变化或其他原因(如索引更名)等,会导致SQL再次出现性能问题。
1.3 Hint的弊端
Hint是比较"暴力"的一种解决方式,不是很优雅。需要开发人员手工修改代码。
Hint不会去适应新的变化。比如数据结构、数据规模发生了重大变化,但使用Hint的语句是感知变化并产生更优的执行计划。
Hint随着数据库版本的变化,可能会有一些差异、甚至废弃的情况。此时,语句本身是无感知的,必须人工测试并修正。
1.4 Hint与注释关系
提示是Oracle为了不破坏和其他数据库引擎之间对SQL语句的兼容性而提供的一种扩展功能。Oracle决定把提示作为一种特殊的注释来添加。它的特殊性表现在提示必须紧跟着DELETE、INSERT、UPDATE或MERGE关键字。
换句话说,提示不能像普通注释那样在SQL语句中随处添加。且在注释分隔符(/*)之后的第一个字符必须是加号。在后面的用法部分,会详细说明。
1.5 语法
- DELETE、INSERT、SELECT和UPDATE是标识一个语句块开始的关键字,包含提示的注释只能出现在这些关键字的后面,否则提示无效。
- "+“号表示该注释是一个提示,该加号必须立即跟在”/*"的后面,中间不能有空格。
- hint是下面介绍的具体提示之一,如果包含多个提示,则每个提示之间需要用一个或多个空格隔开。
- text是其它说明hint的注释性文本
在Oracle中的Hint有许多种,Hive中的Hint尚未广泛了解,以后再补充。
2 使用MapJoin Hint的SQL
2.1 实现参数
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。
2.2 底层执行流程
如上图中的流程,首先是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,就有多少个结果文件
2.3 测试
1、两表关联:
select e.empno,e.ename,e.deptno,d.deptno,d.dname
from emp e join dept d
on e.deptno = d.deptno;
条件:emp表数据为1000条,dept表数据为100条;
说明:执行时间mapjoin会通过元数据(metadata)进行查找,发现是否存在小表(根据hive.mpajoin.samlltable.filesize参数设置,判断是否为小表。此参数默认25M)。
存在小表时将先行进行小表加载到内存中,即dept表,如上HQL语句将dept表加载至内存中,最后再用emp表中数据一条一条与内存中数据进行关联查询。
2、多表关联
select e.empno,e.ename,e.deptno,d.deptno,d.dname
from emp e join dept d join emp_partition c
on e.deptno = d.deptno and e.empno = c.empno;
条件:emp表数据为1000条,dept表数据为100条,emp_partition表数据为500条。
说明:执行时间mapjoin会通过元数据(metadata)进行查找,发现是否存在小表(根据hive.mpajoin.samlltable.filesize参数设置,判断是否为小表。此参数默认25M)。
存在小表时将先行进行小表加载到内存中,即dept、emp_partition表,如上HQL语句将dept表加载至内存中,最后再用emp表中数据一条一条与内存中数据进行关联查询。
注:以上可以对照SQL的执行计划或执行日志更详细,后续补充相关截图及说明