目录
企业级调优
执行计划(explain)
(1)基本语法
EXPLAIN [EXTENDED | DEPENDENCY | AUTHORIZATION] query
explain【extended | dependency | authorization】query
(2)案例
1)查看下面这条语句的执行计划
没有生成MR任务的
explain select * from emp;
有生成MR任务的
explain select deptno,avg(sal) avg_sal from emp group by deptno;
2)查看详细执行计划
没有生成MR任务的
explain extended select * from emp;
有生成MR任务的
explain extended select deptno,avg(sal) avg_sal from emp group by deptno;
fetch抓取
Hive中对某些情况的查询可以不使用MapReduce计算,例如:SELECT * FROM emp;在这种情况下Hive可以简单地读取emp对应的存储目录下的文件,然后输出查询结果到控制台。
在hive-default.xml.template文件中hive.fetch.task.conversion默认是more,老版本hive默认是minimal,该属性修改为more以后在全局查找、字段查找、limit查找等都不走mapreduce程序。
<property>
<name>hive.fetch.task.conversion</name>
<value>more</value>
<description>
Expects one of [none, minimal, more].
0. none : disable hive.fetch.task.conversion
1. minimal : SELECT STAR, FILTER on partition columns, LIMIT only
2. more : SELECT, FILTER, LIMIT only (support TABLESAMPLE and virtual columns)
</description>
</property>
(1)把hive.fetch.task.conversion设置成none,执行查询语句都会执行mapreduce程序。
(2)把hive.fetch.task.conversion设置成more,执行查询语句都不会执行mapreduc程序。
本地模式
大多数的Hadoop Job需要Hadoop提供完整的可扩展性来处理大数据集。
有时Hive的输入数据量非常小,为查询触发执行任务消耗的时间可能会比实际job的执行时间要多。对于大多数这种情况,Hive可以通过本地模式在单台机器上处理所有的任务,对于小数据集来说,执行时间可以明显缩短。
用户可以设置hive.exec.mode.local.auto的值为true,来让Hive在适当的时候自动启动这个优化。
//开启本地mr
set hive.exec.mode.local.auto=true;
//设置local mr的最大输入数据量,当输入数据量小于这个值时采用local mr的方式,默认为134217728即128M
set hive.exec.mode.local.auto.inputbytes.max=50000000;
//设置local mr的最大输入文件个数,当输入文件个数小于这个值时采用local mr的方式,默认为4
set hive.exec.mode.local.auto.input.files.max=10;
表的优化
小表大表join(mapjoin)
将key相对分散并且数据量小的表放在join的左边,可以使用map join让小的维度表先进内存。在map端完成 join。
实际测试发现:新版的hive已经对小表join大表和大表join小表进行了优化。小表放在左边和右边已经没有区别。
(1)设置自动选择mapjoin
//默认为true
set hive.auto.convert.join = true;
(2)小表大表的阈值设置,默认小于25M为小表
set hive.mapjoin.smalltable.filesize = 25000000;
(3)mapjoin工作机制
大表join大表
(1)空key过滤
有时join超时是因为某些key对应的数据太多,相同key对应的数据都会发送到相同的reducer上从而导致内存不够。此时我们应该仔细分析这些异常的key,很多情况下,这些key对应的数据是异常数据,我们需要在SQL语句中进行过滤。例如:key对应的字段为空。
1)配置历史服务器(mapred-site.xml)
<property>
<name>mapreduce.jobhistory.address</name>
<value>hadoop01:10020</value>
</property>
<property>
<name>mapreduce.jobhistory.webapp.address</name>
<value>hadoop01:19888</value>
</property>
启动历史服务器
sbin/mr-jobhistory-daemon.sh start historyserver
查看jobhistory
http://hadoop01:19888/jobhistory
2)不过滤空key
nullidtable为空id表,bigtable为非空id表,jointable为目标表
insert overwrite table jointable select n.* from
nullidtable n left join bigtable b on n.id = b.id;
3)过滤空key
nullidtable为空id表,bigtable为非空id表,jointable为目标表
insert overwrite table jointable select n.* from (select * from
nullidtable where id is not null) n left join bigtable b on n.id = b.id;
(2)空key转换
有时虽然某个key为空对应的数据很多,但相应的数据不是异常数据必须包含在join的结果中,此时我们可以把key为空的字段赋一个随机的值,使得数据随机均匀地分布到不同的reducer上。
1)设置X个reduce个数
set mapreduce.job.reduces = X;
2)join两张表
nullidtable为空id表,bigtable为非空id表,jointable为目标表
不随机发布空值
insert overwrite table jointable select n.* from nullidtable n
left join bigtable b on n.id = b.id;
随机发布空值
insert overwrite table jointable select n.* from nullidtable n
full join bigtable b on nvl(n.id,rand()) = b.id;
不随机发布空值会出现数据倾斜,某些reducer的资源消耗远大于其他reducer;
随机发布空值消除了数据倾斜,负载均衡reducer的资源消耗。
(3)SMB(Sort Merge Bucket join)
group by
默认情况下,Map阶段同一Key数据会发给同一reduce,当一个key数据过大时就倾斜了。
并不是所有的聚合操作都需要在Reduce端完成,很多聚合操作都可以先在Map端进行部分聚合,最后在Reduce端得出最终结果。
(1)开启map端聚合参数设置
1)是否在map端进行聚合,默认为true
set hive.map.aggr = true
2)在map端进行聚合操作的条目数目
set hive.groupby.mapaggr.checkinterval = 100000
3)有数据倾斜时进行负载均衡,默认为false
set hive.groupby.skewindata = true
当选项设定为true,生成的查询计划会有两个MR Job。
第一个MR Job中,Map的输出结果会随机分布到Reduce中,每个Reduce做部分聚合操作,并输出结果,这样处理的结果是相同的Group By Key有可能被分发到不同的Reduce中,从而达到负载均衡的目的;
第二 个MR Job再根据预处理的数据结果按照Group By Key分布到Reduce中,这个过程可以保证 相同的Group By Key被分布到同一个Reduce中,最后完成最终的聚合操作。
没有优化
select deptno from emp group by deptno;
优化后
set hive.groupby.skewindata = true;
select deptno from emp group by deptno;
count(distinct)去重统计
数据量小的时候无所谓,数据量大的情况下,由于COUNT DISTINCT操作需要用一个Reduce Task来完成,这一个Reduce需要处理的数据量太大,就会导致整个Job很难完成,一般COUNT DISTINCT使用先GROUP BY再COUNT的方式替换,但是需要注意group by造成的数据倾斜问题。
(1)执行去重id查询
select count(distinct id) from bigtable;
(2)采用group by去重id查询
select count(id) from (select id from bigtable group by id) a;
注:虽然会多用一个Job来完成,但在数据量大的情况下这个绝对是值得的。
笛卡尔积
尽量避免笛卡尔积,join的时候不加on条件或者无效的on条件,Hive只能使用1个reducer来完成笛卡尔积。
行列过滤
列处理:在SELECT中只拿需要的列,如果有分区,尽量使用分区过滤,少用SELECT *。
行处理:在分区剪裁中,当使用外关联时,如果将副表的过滤条件写在Where后面,会先全表关联,之后再过滤。
比如:
select o.id from bigtable b join bigtable o on o.id = b.id
where o.id <= 10;
应该使用:
select b.id from bigtable b join
(select id from bigtable where id <= 10) o on b.id = o.id;
合理设置map及reduce数
通常情况下,作业会通过input的目录产生一个或者多个map任务。主要的决定因素有:input的文件总个数,input的文件大小,集群设置的文件块大小。
问题1:是不是map数越多越好?
答:不是。如果一个任务有很多小文件(远远小于块128m),则每个小文件也会被当做一个块,用一个map任务来完成,而一个map任务启动和初始化的时间远远大于逻辑处理的时间,就会造成很大的资源浪费。而且,同时可执行的map数是受限的。
问题2:是不是保证每个map处理接近128m的文件块,就高枕无忧了?
答:不一定。比如有一个127m的文件,正常会用一个map去完成,但这个文件只有一个或者两个小字段,却有几千万的记录,如果map处理的逻辑比较复杂,用一个map任务去做,肯定也比较耗时。
针对上面的问题,我们需要采取两种方式来解决:即减少map数和增加map数
复杂文件增加map数
当input的文件都很大、任务逻辑复杂、map执行非常慢的时候,可以考虑增加Map数来使得每个map处理的数据量减少,从而提高任务的执行效率。
增加map的方法为:
根据computeSliteSize(Math.max(minSize,Math.min(maxSize,blocksize)))=blocksize=128M公式调整maxSize最大值。让maxSize最大值低于blocksize就可以增加map的个数。
案例:
(1)设置最大切片值
设置最大切片值为100字节
set mapreduce.input.fileinputformat.split.maxsize=100;
(2)查看
设置前查看
select count(*) from emp;
设置后查看
select count(*) from emp;
小文件进行合并
(1)在map执行前合并小文件可以减少map数,CombineHiveInputFormat具有对小文件进行合并的功能(系统默认的格式)。HiveInputFormat没有对小文件合并功能。
set hive.input.format = org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
(2)设置在Map-Reduce任务结束时合并小文件
在map-only任务结束时合并小文件,默认true
set hive.merge.mapfiles = true;
在map-reduce任务结束时合并小文件,默认 false
set hive.merge.mapredfiles = true;
合并文件的大小,默认256M
set hive.merge.size.per.task = 268435456;
当输出文件的平均大小小于该值时,启动一个独立的map-reduce任务进行文件merge
set hive.merge.smallfiles.avgsize = 16777216;
合理设置reduce数
(1)调整reduce个数方法一
1)每一个reduce处理的数据量默认是256M
hive.exec.reducers.bytes.per.reducer=256000000
2)每个任务最大的reduce数默认为1009
hive.exec.reducers.max=1009
3)计算reduce数公式
N=min(参数 2,总输入数据量/参数 1)
(2)调整reduce个数方法二
在Hadoop的mapred-default.xml中修改
设置每个job的reduce个数
set mapreduce.job.reduces = 15;
(3)reduce个数并不是越多越好
1)过多的启动和初始化reduce也会消耗时间和资源。
2)有多少个reduce就会有多少个输出文件,如果生成了很多个小文件,这些小文件作为下一个任务的输入,则也会出现小文件过多的问题;
在设置reduce个数的时候也需要考虑这两个原则:
处理大数据量利用合适的reduce数;
使单个reduce任务处理数据量大小要合适;
并行执行
Hive会将一个查询转化成一个或者多个阶段。这样的阶段可以是MapReduce阶段、抽样阶段、合并阶段、limit阶段,或者Hive执行过程中可能需要的其他阶段。
默认情况下,Hive一次只会执行一个阶段。不过,某个特定的job可能包含众多的阶段,而这些阶段可能并非完全互相依赖的,也就是说有些阶段是可以并行执行的,这样可能使得整个job的执行 时间缩短。如果有更多的阶段可以并行执行,那么job可能就越快完成。
通过设置参数hive.exec.parallel值为true,就可以开启并发执行。
set hive.exec.parallel=true;
在共享集群中,如果job中并行阶段增多,集群利用率就会增加。当然,得是在系统资源比较空闲的时候才有优势,没资源,并行也无用。。
严格模式
hive可以通过设置防止一些危险操作。
(1)分区表不使用分区过滤
将hive.strict.checks.no.partition.filter设置为true时,对于分区表,除非where语句中含有分区字段过滤条件来限制范围,否则不允许执行。换句话说,就是用户不允许扫描所有分区。进行这个限制的原因是,通常分区表拥有非常大的数据集,而且数据增加迅速。没有进行分区限制的查询可能会消耗令人不可接受的巨大资源来处理这个表。
set hive.strict.checks.no.partition.filter=true;
(2)使用order by没有limit过滤
将hive.strict.checks.orderby.no.limit设置为true时,对于使用了order by语句的查询,要求必须使用limit语句。因为order by为了执行排序过程会将所有的结果数据分发到同一个Reducer中进行处理,强制要求用户增加这个limit语句可以防止Reducer额外执行很长一段时间。
set hive.strict.checks.orderby.no.limit=true;
(3)笛卡尔积
将hive.strict.checks.cartesian.product设置为true时会限制笛卡尔积的查询。对关系型数据库非常了解的用户可能期望在执行JOIN查询的时候不使用ON语句而是使用where语句,这样关系数据库的执行优化器就可以高效地将WHERE语句转化成那个ON语句。不幸的是,Hive并不会执行这种优化;如果表足够大,那么这个查询就会出现不可控的情况。
set hive.strict.checks.cartesian.product=true;
本文为学习笔记!!!