第一原则先降数据量再join
set hive.exec.parallel=true;
set hive.exec.parallel.thread.number=8;
hive默认job是顺序进行的,一个HQL拆分成多个job,job之间无依赖关系也没有相互影响可以并行执行
对于同一个sql来说同时可以运行的job的最大值,该参数默认为8.此时最大可以同时运行8个job
set hive.exec.dynamic.partition=true;
set hive.exec.dynamic.partition.mode=nonstrict
启动动态分区功能
set mapred.job.reuse.jvm.num.tasks=10;
jvm的启动过程可能会造成相当大的开销,对于单个执行任务时间较短时候,频繁开启JVM将是很大的开销,开启JVM重用将会一直占用使用到的task插槽,以便进行重用,直到任务完成后才能释放。
一般分为join引起和group by引起分别解决。
操作 | 原因 | 现象 |
---|---|---|
Group by | 分组key集中 | 处理某key值的reduce非常耗时 |
Join | 关联key集中(例如关联字段空值过多) | 处理某key值的reduce非常耗时 |
分两方面优化:
第一个:
set hive.map.aggr=true;
set hive.groupby.mapaggr.checkinterval=100000;
set hive.map.aggr.hash.min.reduction=0.5;
hive.map.aggr=true(默认)参数控制在group by的时候是否map局部聚合,但也不是都会局部聚合,如果聚合前后差别不是很大,聚合也就没什么意义了。
后两个设置是判断是否需要做map局部聚合,即:预先取100000条数据聚合,如果聚合后的条数/100000>0.5,则不再聚合。
第二个:
set Hive.groupby.skewindata=true;
控制启动两个MR Job完成,第一个Job先不按GroupBy字段分发,而是随机分发做一次聚合,然后启动第二个Job,拿前面聚合过的数据按GroupBy字段分发计算出最终结果。但是否生效还存在限制,详情见 Hive-hive.groupby.skewindata配置相关问题调研
优化主要分两个方向:skew join和重写业务逻辑
set hive.optimize.skewjoin=true;
set hive.skewjoin.key=100000;
记录超过hive.skewjoin.key(默认100000)阈值的key值先写入hdfs,然后再进行一个map join的job任务,最终和其他key值的结果合并为最终结果。
这个需要结合具体的场景重写,例如:在日志表与用户表关联时候(通过user_id关联),直接关联可能导致user_id为null的发生数据倾斜,此时可以把日志表中user_id为null的单独处理,如下:
SELECT a.xx, b.yy FROM log a JOIN users b
ON a.user_id IS NOT NULL
AND a.user_id = b.user_id
UNION ALL
SELECT a.xx, NULL AS yy FROM log a WHERE a.user_id IS NULL;
set.hive.auto.convert.join=true;
默认值是25mb,小表小于25mb自动启动mapjoin
select /*+mapjoin(A)*/ f.a,f.b from A t join B f on (f.a=t.a)
其中,A为小表,将A表复制到所有节点
通过调整max可以起到调整map数的作用,减小max可以增加map数,增大max可以减少map数。
需要提醒的是,直接调整mapred.map.tasks这个参数是没有效果的。
mapred.min.split.size
: 指的是数据的最小分割单元大小;min的默认值是1B
mapred.max.split.size
: 指的是数据的最大分割单元大小;max的默认值是256MB
reduce个数的设定极大影响任务执行效率,不指定reduce个数的情况下,Hive会猜测确定一个reduce个数,基于以下两个设定:
hive.exec.reducers.bytes.per.reducer
(每个reduce任务处理的数据量,默认为1000^3=1G)
hive.exec.reducers.max
(每个任务最大的reduce数,默认为999)
计算reducer数的公式很简单N=min(参数2,总输入数据量/参数1)
调节的办法
set hive.exec.reducers.bytes.per.reducer=500000000;
调整hive.exec.reducers.bytes.per.reducer参数的值;
set mapred.reduce.tasks=15;
调整mapred.reduce.tasks参数的值;
设置map输入的小文件合并
set mapred.max.split.size=256000000;
//一个节点上split的至少的大小(这个值决定了多个DataNode上的文件是否需要合并)
set mapred.min.split.size.per.node=100000000;
//一个交换机下split的至少的大小(这个值决定了多个交换机上的文件是否需要合并)
set mapred.min.split.size.per.rack=100000000;
//执行Map前进行小文件合并
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
设置map输出和reduce输出进行合并的相关参数
//设置map端输出进行合并,默认为true
set hive.merge.mapfiles = true
//设置reduce端输出进行合并,默认为false
set hive.merge.mapredfiles = true
//设置合并文件的大小
set hive.merge.size.per.task = 256*1000*1000
//当输出文件的平均大小小于该值时,启动一个独立的MapReduce任务进行文件merge。
set hive.merge.smallfiles.avgsize=16000000