一,调优基础 :
1 ,连接暗示 :
- 需求 : 让 join 发生在 map 端
- sql :
select /*+ mapjoin(customers) */ a.*,b.*
from customers a left outer join orders b
on a.id=b.cid;
2 ,hive 执行计划
hql 在执行的时候,是转化成了什么样的 mr 去执行的。
3 ,查看执行计划 : explain
select count(1) from wc;
explain select count(1) from wc;
4 ,SQL 转化为 MapReduce 的过程
- Antlr 定义 SQL 的语法规则,完成 SQL 词法,语法解析,将 SQL 转化为抽象语法树 AST Tree
- 遍历 AST Tree,抽象出查询的基本组成单元 QueryBlock
- 遍历 QueryBlock,翻译为执行操作树OperatorTree
- 逻辑层优化器进行 OperatorTree 变换,合并不必要的 ReduceSinkOperator,减少 shuffle 数据量
- 遍历 OperatorTree,翻译为 MapReduce 任务
- 物理层优化器进行 MapReduce 任务的变换,生成最终的执行计划
二 ,调优 :
1 ,不用 select *
2 ,不要出现 笛卡尔积
join … on …
3 ,数据量大的情况下,减少 count(distinct)
原因 : count(distinct) 容易产生倾斜问题。
4 ,减少 map 数 :每个文件对应一个 map 任务
- 已知 : 194 个文件,其中很多是远远小于 128m 的小文件,总大小 9G 。
- Map 总共消耗的计算资源: SLOTS_MILLIS_MAPS = 623,020k ( )
- 调优 :
1. 1 ,小文件合并 :这个参数表示执行前进行小文件合并
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
2 ,小文件合并参数 :
// 文件块最大值
set mapred.max.split.size=100000000; ( 100M )
// 一个节点上 split 至少的大小 ( 文件块最小值 )
set mapred.min.split.size.per.node=100000000;
// 一个交换机下split的至少的大小
set mapred.min.split.size.per.rack=100000000;
3 ,底层代码 : 解释 set mapred.max.split.size
minSize=max{minSplitSize,mapred.min.split.size} (minSplitSize大小默认为1B)
maxSize=mapred.max.split.size(不在配置文件中指定时大小为Long.MAX_VALUE)
splitSize=max{minSize,min{maxSize,blockSize}}
- 结果 :
1 ,大于文件块大小 128m 的,按照 128m 来分隔。
2 ,小于 128m ,大于 100m 的,按照 100m 来分隔。
3 ,把那些小于 100m 的(包括小文件和分隔大文件剩下的),进行合并。
4 ,最终生成了74个块。 - 消耗 :
map 消耗的计算资源:SLOTS_MILLIS_MAPS= 333,500
5 ,增加 map 数 : 某些情况并不是任务数越少越好
- 在第四点中 :我们分析,为了节省资源,不能让很多小文件产生,增加我们的 mr 任务数。
- 但是 :如果有这么一个文件,他只有两列 ( id int,name string ),但是,他有几千万条记录,这种情况,很明显,不能让一个 map 去处理,就需要增加任务数。
- 做法 : 虽然只有一个 map ,但是我们可以设置多个 reduce 。
set mapred.reduce.tasks=10;
create table a_1 as
select * from a
distribute by rand(123); - 结果 :
先分文件再执行,distribute 就是分文件的意思
6 ,指定 reduce 个数 :例如 15 个
set mapred.reduce.tasks = 15;
7 ,不指定 reduce 个数 :
设定以下两个限制条件 :
hive.exec.reducers.bytes.per.reducer :每个 reduce 任务处理的数据量,默认为 1G 。
hive.exec.reducers.max :每个任务最大的 reduce 数,默认为 999
8 ,hive 严格模式 : 防止大资源消耗性查询
默认 :严格模式
严格模式 : 防止用户执行那些可能产生意想不到的不好的效果的查询。
可以禁止 3 种类型的查询。
设置严格模式 :
set hive.mapred.mode=strict;
禁止的三种类型 :
1 ,分区表不能扫描全表 : 查询时,必须加分区字段,也就是不能查询所有数据。
2 ,order by 必须与 limit 连用 :全表排序将会是一项巨大的工程,因此必须加以限制。
3 ,限制笛卡尔积的查询 :必须 join on 连用,不可以 join - where 连用。
9 ,hive 本地模式 ( 测试用,小任务用 )
- 原理 :对于小数据集,本地模式的执行时间会明显被缩短。
- 适用场景 : 必须满足以下条件
1 ,job 的输入数据大小必须小于参数:hive.exec.mode.local.auto.inputbytes.max(默认128MB)
2 ,job 的 map 数必须小于参数:hive.exec.mode.local.auto.tasks.max(默认4)
3 ,job 的 reduce 数必须为 0 或者 1 - 设置本地模式 :
set hive.exec.mode.local.auto=true;(默认为false)
查看本地模式是否打开 :set hive.exec.mode.local.auto - 测试 :select count(1) from emp; 不使用本地模式 : 22 秒
- 使用本地模式 : 3 秒
10 ,hive 推测执行
- mr 的推测执行 :数据倾斜的时候,有的任务执行时间较长,apm 默认推测此任务出现问题,另启一个任务进行执行,以先执行完毕的结果为准,
- sql 不支持 :
使用SQL语句时将其关闭 - 设置 : Hadoop的mapred-site.xml
<property>
<name>mapreduce.map.speculative</name>
<value>true</value>
<description>
If true, then multiple instances of some map tasks
may be executed in parallel.
</description>
</property>
<property>
<name>mapreduce.reduce.speculative</name>
<value>true</value>
<description>If true, then multiple instances of some reduce tasks
may be executed in parallel.
</description>
</property>
- 设置 : hive 端
<property>
<name>hive.mapred.reduce.tasks.speculative.execution</name>
<value>true</value>
<description>
Whether speculative execution for reducers
should be turned on.
</description>
</property>
11 ,JVM 重用
- 设置 ( 比如 10 个 ) : set mapred.job.reuse.jvm.num.tasks=10;
- 适用场景 :很多小文件的场景或者 task 特别多的场景,这类场景大多数执行时间都很短。
- 原理 :
1 ,很多小文件的场景或者 task 特别多的场景,这类场景大多数执行时间都很短。
2 ,hadoop 默认配置是使用派生 JVM 来执行 map 和 reduce 任务的。
3 ,这时 jvm 的启动过程可能会造成相当大的开销,尤其是执行的 job 包含有成千上万个 task 任务的情况。
12 ,并行执行
- 原理 : 同步执行hive的多个阶段,hive在执行过程,将一个查询转化成一个或者多个阶段。某个特定的job可能包含众多的阶段,而这些阶段可能并非完全相互依赖的,也就是说可以并行执行的,这样可能使得整个job的执行时间缩短
- 设置 :set hive.exec.parallel=true;