任务进度长时间维持在99%(或100%),查看任务监控页面,发现只有少量(1个或几个)reduce子任务未完成。因为其处理的数据量和其他reduce差异过大。
原因:某个reduce的数据输入量远远大于其他reduce数据的输入量
1)、key分布不均匀
2)、业务数据本身的特性
3)、建表时考虑不周
解决方案:
(1)参数调节
set hive.map.aggr=true
--在map端中会做部分聚集操作,效率更高但需要更多的内存,可以根据自己企业的资源情况来设置,
set hive.groupby.skewindata=true
--有数据倾斜的时候进行负载均衡 ,决定group by操作是否支持倾斜数据,其实说白了就相当于MR中的conbiner做了一次预聚合。
注意:只能对单个字段聚合。
控制生成两个MR Job,第一个MR Job Map的输出结果随机分配到reduce中减少某些key值条数过多某些key条数过小造成的数据倾斜问题。
在第一个 MapReduce 中,map 的输出结果集合会随机分布到 reduce 中, 每个reduce 做部分聚合操作,并输出结果。这样处理的结果是,相同的 Group By Key 有可能分发到不同的reduce中,从而达到负载均衡的目的;
第二个 MapReduce 任务再根据预处理的数据结果按照 Group By Key 分布到 reduce 中(这个过程可以保证相同的 Group By Key 分布到同一个 reduce 中),最后完成最终的聚合操作
(2) 可利用mapjoin进行优化(不适用所有场景)
小表在join左侧,大表在右侧,或使用mapjoin 将小表加载到内存中。然后再对比较大的表进行map操作。
join就发生在map操作的时候,这里的join并不会涉及reduce操作。map端join的优势就是在于没有shuffle,从而提升效率。
select /*+ MAPJOIN(a) */
* from a join b
where a.id = b.id;
(3) 熟悉数据的分布,优化sql的逻辑,找出数据倾斜的原因。
举例1:所有的空值都会被发送到同一个reduceTask去处理,造成数据倾斜。
空值过滤,不参与运算或进行赋值随机数。
if(id is null,cast(rand()*-100 as bigint),id)
举例2:加盐去盐操作,使数据分散
select count(distinct id) as num from table_name
--优化1:
select count(id) as num from (select id from table_name group by id) a;
--优化2:
select sum(tag_num) as num
from
(select tag
,count(id) as tag_num--每个标记下的个数
from
(select id
,CAST(RAND() * 100 AS BIGINT) tag --随机打上标记,标记为:0-100之间的整数
from table_name
group by id
) t
group by tag
) t
;
--优化3:
select sum(part_num) as num
from
(select substr(id, 1, 3) as id_part --截取id部分做维度
,COUNT(DISTINCT substr(id, 4)) AS part_num --每个被截取的id下的个数
from table_name
group by substr(id, 1, 3)
) t
;
小文件的产生有三个地方,map输入,map输出,reduce输出,小文件过多也会影响hive的分析效率:
1、设置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;
2、设置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
3、通过直接对系统文件进行操作来合并小文件