1.合理选择排序
排序算法比较耗资源,应根据业务需要选择
- order by :全局排序,大数据集会消耗太过漫长的时间
- sort by:局部排序,只能保证每个reducer的输出数据都是有序的
- distribute by:分桶不排序,控制map的输出在reducer中是如何划分的,若需排序,则+sort by[字段]
- cluster by:分桶且排序
2.慎用笛卡尔积
与join关联不同,笛卡尔积没有关联条件,转换成mr程序的时候,所有数据都进入一个reducetask中,性能极低。
左表:stu 右表:sco 右表:sco_copy
huang math 1 music 1 music 2
meng chinese 2 read 1 read 2
jun english 1
zheng math 2
fan it 1
若无法避免,则需要增加reducetask的个数:
1、假设需要增加reducetask的个数为2,将右表复制一个,形成两个右表;
create table sco_copy as select * from sco;
2、为右表增加关联键index,右表1的关联键均等于1,右表2的关联键均等于2;
3、为左表增加关联键index,根据1、2、1、2...的顺序添加,可防止数据倾斜;
将左表的大表分解成两部分,分别对两个一样的右表进行关联键匹配,增加reducetask的并行度,提高效率。
3.合并小文件
小文件过多会导致maptask的个数过多,则导致container的启动和销毁的次数过多,启动销毁的时间远远大于程序运行的时间。
(输入合并)原始数据中的小文件过多,合并。
对应参数:
set mapred.max.split.size=256000000; #每个Map最大输入大小 set mapred.min.split.size.per.node=100000000; #一个节点上split的至少的大小 set mapred.min.split.size.per.rack=100000000; #一个交换机下split的至少的大小
执行Map前进行小文件合并(常用)
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
在开启了org.apache.hadoop.hive.ql.io.CombineHiveInputFormat后,一个data node节点上多个小文件会进行合并,合并文件数由mapred.max.split.size限制的大小决定。
mapred.min.split.size.per.node决定了多个data node上的文件是否需要合并~
mapred.min.split.size.per.rack决定了多个交换机上的文件是否需要合并~
(输出合并)当hql中存在多个mr程序串联的时候,上个mr程序的reducetask的个数为3,则会输出3个文件,要合并后进入下一个mr程序。
set hive.merge.mapfiles = true #在Map-only的任务结束时合并小文件 set hive.merge.mapredfiles = true #在Map-Reduce的任务结束时合并小文件 set hive.merge.size.per.task = 256*1000*1000 #合并文件的大小
当输出文件的平均大小小于该值时,启动一个独立的map-reduce任务进行文件merge
set hive.merge.smallfiles.avgsize=16000000
4.合理设计maptask的个数(最好128M一个)
个数过多,效率低主要是container的启动和销毁时间耽误的;
若是小文件过多导致的,则执行上面的合并小文件过程即可;
若是分片计算导致过多的,则需要设置MaxInputSplitSize(最大数据分片大小)为一个大于默认blockSize的值,越大map数量越少。
FileInputFormat.setMaxInputSplitSize(job,157286400); #默认值是134217728(128M),157286400(150M)
或者设置一个container中运行多个maptask任务
set mapred.job.reuse.jvm.num.tasks = 5;设置可以运行5个maptask任务
个数过少,导致map端的并行度不高,资源没有充分利用,大量工作时容易堵塞集群;
设置MaxInputSplitSize(最大数据分片大小)为一个小于默认blockSize的值,越小map数量越多。
FileInputFormat.setMaxInputSplitSize(job,104857600); #默认值是134217728(128M),104857600(100M)
5.合理设计reducetask的个数
reducetask决定的是map端出来的数据的走向
经验值:上限: reducetask*0.95
在没有达到上限之前 根据map端输出的数据量和本身业务决定最终输出的reducetask的个数
reducetask一旦设定 内部的分区:key.hash%reducetask
6.尽量不要使用in/exists,用left semi join代替
因为in/exists没有关联条件,而semi join有,hive擅长有关联条件的关联。
7.多表插入时,使用多重查询,不要使用单重
单重:每插入一个表就要全表扫描一次stu表
insert into table stu01 select * from stu where age = 18; insert into table stu02 select * from stu where age = 19;
多重:仅扫描一次stu表
from stu insert into table stu01 select * where age = 18, insert into table stu02 select * where age = 19;
8.合理设计分区
为了减少全表扫描所耗费的时间,将一些频繁作为筛选条件的字段,设置为分区字段;
select * from stu where age =18;
未分区的时候,需要扫描stu全表;
以age字段分区后,仅扫描age=18的子表;
9.合理设计分桶
分桶的作用:
- 提高join的性能,分桶前:大表 join 大表 ; 分桶后:小表 join 小表;
- 提高抽样的性能,分桶是按照hash算法分的,使得数据足够散列;
怎么抽样?语法:
tablesample(bucket x out of y);#y:桶簇的个数,x:选择第几个桶簇
举例:
1、stu表分了6个桶存储,将stu分成6个桶簇,取第1个桶簇的数据作为样本
select * from stu tablesample(bucket 1 out of 6);
6个桶分成6个桶簇,则一个桶簇中正好是一个完整的桶,取第1个桶簇等同于取第一个桶中的数据。
2、stu表分了6个桶存储,将stu分成12个桶簇,取第1个桶簇的数据作为样本
select * from stu tablesample(bucket 1 out of 12);
6个桶分成12个桶簇,则一个桶簇中只有半个桶,取第1个桶簇等同于取第一个桶中的上一半数据
3、stu表分了6个桶存储,将stu分成3个桶簇,取第1个桶簇的数据作为样本
select * from stu tablesample(bucket 1 out of 3);
6个桶分成3个桶簇,则一个桶簇中有两个桶,但是,第一个桶簇中并非是第1个桶和第2个桶的数据,而是第1个和第1+3=4个桶中的数据,以此类推:
- 第 1 个桶簇:第 1 桶 + 第 4 桶的数据;
- 第 2 个桶簇:第 2 桶 + 第 5 桶的数据;
- 第 3 个桶簇:第 3 桶 + 第 6 桶的数据;
10.join的优化
1、先过滤再join匹配,最大限度的减少参与join匹配的数据量;
2、大表 join 小表 ,最好启动mapjoin
如果没有启动MapJoin,那么Hive解析器会在Reduce阶段完成join,整个过程包含Map、Shuffle、Reduce阶段,在在Shuffle阶段时要进行的大量数据传输,若在Map阶段完成join,则节省了数据传输的时间。
为了让小表和大表可以进入同一个map中处理,可以小表文件复制到每一个Map任务的本地,再让Map把文件读到内存中待用。
参考文章:https://www.cnblogs.com/qiuhong10/p/7698277.html
https://blog.csdn.net/shudaqi2010/article/details/89505599
在Hive0.11后,可以通过以下两个属性来设置该优化的触发时机
set hive.auto.convert.join=true; hive.mapjoin.smalltable.filesize=25000000 ; #25M