文章目录
说到hive的数据倾斜,可能有的小伙伴还不了解什么是数据倾斜,所以咱们就从hive数据倾斜的表现、hive数据倾斜发生的原因、hive数据倾斜的解决方案三个方面来聊一聊hive的数据倾斜
hive数据倾斜的表现
我们都知道hive的底层其实是mr(MapReduce)引擎,hsql其实就是把sql语言转换成mr去运行,这样就大大缩减了咱们去写mr的时间,然而有时候咱们会发现在你运行一个任务的时候,明明所有的map task都完成了,并且99%的reduce task也完成,只剩下一个后者少数几个reduce task一直在执行,等了半天就是不动,其实这种情况一般都是发生了数据倾斜,说白了,hive的数据倾斜本质上就是MapReduce的数据倾斜(感兴趣的小伙伴可以去查看任务监控页面)
hive数据倾斜的原因
数据倾斜的原因很大部分是join倾斜和聚合倾斜两大类,其实数据倾斜这个问题,在MapReduce编程模型中十分常见,根本原因就是大量相同的key被分配到一个reduce里,造成一个reduce任务累死了,但是其他的reduce任务闲死。下面咱们来罗列一下常见的数据倾斜有哪些原因:
1、key分布不均衡
2、业务问题后者业务数据本身的问题,某些数据比较集中
3、建表的时候考虑不周
4、某些sql语句本身就有数据倾斜,例如:
(1)大表join小表:其实小表的key集中,分发到某一个或者几个reduce上的数据远远高于平均值
(2)大表join大表:空值或无意义值:如果缺失的项很多,在做join时这些空值就会非常集中,拖累进度。
(3)group by: group by的时候维度过小,某值的数量过多,处理某值的reduce非常耗时间。
(4)Count distinct:某特殊值过多,处理此特殊值的reduce耗时。
Hive数据倾斜解决
Hive倾斜之group by聚合倾斜
-
原因:
分组的维度过少,每个维度的值过多,导致处理某值的reduce耗时很久;
对一些类型统计的时候某种类型的数据量特别多,其他的数据类型特别少。当按照类型进行group by的时候,会将相同的group by字段的reduce任务需要的数据拉取到同一个节点进行聚合,而当其中每一组的数据量过大时,会出现其他组的计算已经完成而这个reduce还没有计算完成,其他的节点一直等待这个节点的任务执行完成,所以会一直看到map 100% reduce99%的情况; -
解决方法:有数据倾斜的时候进行负载均衡
set hive.map.aggr=true;
set hive.groupby.skewindata=true;
- 原理:
hive.map.aggr=true
这个配置代表开启map端聚合;
hive.groupby.skewindata=true
,当选项设定为true,生成的查询计划会有两个MRJob。第一个MRJob中,Map的输出结果集合会随机分布到Reduce中,每个Reduce做部分聚合操作,并输出结果,这样处理的结果是相同于Group By Key 有可能被分发到不同的Reduce中,从而达到负载均衡的目的;第二个MRJob再根据预处理的数据结果按照Group By Key分布到Reduce中(这个过程可以保证相同的Group By Key被分布到同一个Reduce中),最后完成最终的聚合操作。
Hive倾斜之Map和Reduce优化
-
原因1:当出现小文件过多,需要合并小文件。可以通过
set hive.merge.mapredfiles=true
来解决; -
原因2:输入数据存在大块和小块的严重问题,比如 说:一个大文件128M,还有1000个小文件,每 个1KB。 解决方法:任务输入前做文件合并,将众多小文件合并成一个大文件。通过
set hive.merge.mapredfiles=true
解决; -
原因3:单个文件大小稍稍大于配置的block块的大小,此时需要适当增加map的个数。解决方法:
set mapred.map.tasks
的个数; -
原因4:文件大小适中,但是map端计算量非常大,如:
select id,count(*),sum(case when…),sum(case when …)…
需要增加map个数。解决方法:set mapred.map.tasks
个数,set mapred.reduce.tasks
个数;
Hive倾斜之HQL中包含count(distinct)时
- 原因:如果数据量非常大,执行如
select a,count(distinct b) from t group by a;
类型的sql时,会出现数据倾斜的问题。 - 解决方法:使用sum…group by代替。如:
select a,sum(1) from(select a,b from t group by a,b) group by a;
Hive倾斜之HQL中join优化
- 原因:当遇到一个大表和一个小表进行join操作时。使用mapjoin将小表加载到内存中。如:
select /*+ MAPJOIN(a) */ a.c1, b.c1,b.c2 from a join b where a.c1 = b.c1;
遇到需要进行join,但是关联字段有数据为null,如表一的id需要和表二的id进行关联;
- 解决方法1:id为null的不参与关联,比如:
select * from log a
join users b
on a.id is not null and a.id = b.id
union all
select * from log a
where a.id is null;
- 解决方法2: 给null值分配随机的key值,比如:
select * from log a
left outer join users b
on case when a.user_id is null
then concat('hive',rand())
else a.user_id end = b.user_id;
合理设置Map数
- 大小表join的时候:使用map join 让小的维度表先进内存,在map端完成reduce。效率很高。
- 大表join大表的时候:把空值的key变成一个字符串加上随机数,把倾斜的数据分到不同的reduce上,由于null值关联不上,处理后不影响最终的结果。
对上文描述的总结
1)通常情况下,作业会通过input的目录产生一个或者多个map任务。
主要的决定因素有:input的文件总个数,input的文件大小,集群设置的文件块大小。
2)是不是map数越多越好?
答案是否定的。如果一个任务有很多小文件(远远小于块大小128m),则每个小文件也会被当做一个块,用一个map任务来完成,而一个map任务启动和初始化的时间远远大于逻辑处理的时间,就会造成很大的资源浪费。而且,同时可执行的map数是受限的。
3)是不是保证每个map处理接近128m的文件块,就高枕无忧了?
答案也是不一定。比如有一个127m的文件,正常会用一个map去完成,但这个文件只有一个或者两个小字段,却有几千万的记录,如果map处理的逻辑比较复杂,用一个map任务去做,肯定也比较耗时。
针对上面的问题2和3,我们需要采取两种方式来解决:即减少map数和增加map数;
ODPS MR底层的处理逻辑可以概括为如下几个步骤:
任务提交:当用户将MR任务提交到ODPS时,ODPS会调度资源,并在分布式计算集群上启动相应数量的Mapper和Reducer任务。
数据切分:输入数据会被切分成若干个数据块,每个数据块都会被分配到一个Mapper任务进行处理。切分策略可以根据数据大小、数据格式等进行配置。
Map阶段:每个Mapper任务会读取自己负责处理的数据块,将数据解析成key-value对,并将结果输出到临时存储区。
Shuffle阶段:在Map阶段结束后,系统会将Mapper任务的输出结果进行合并、排序和重分区,以便于传输给Reducer任务。Shuffle阶段的主要目标是确保相同key的数据能够被发送到同一个Reducer任务。
Reduce阶段:每个Reducer任务会接收到一部分经过Shuffle阶段处理的数据,对这些数据进行聚合和处理,并将最终结果输出到输出数据表中。
数据合并:多个Reducer任务的输出结果会被合并成最终的输出数据。数据的合并方式可以根据应用需求进行配置,如合并成一个文件或多个文件。
结果输出:最终的输出数据会被保存到用户指定的数据表中,可以供后续的查询、分析和存储使用。
在整个ODPS MR底层处理逻辑中,数据的输入、切分、Map、Shuffle、Reduce等阶段都是高度并行化的,以便于处理大规模的数据集。同时,ODPS还会自动进行任务调度、资源分配、容错恢复和监控等管理操作,确保任务的顺利执行和数据的准确性。
参考:https://blog.csdn.net/wind96/article/details/127696043
https://blog.csdn.net/weixin_43542605/article/details/122326059
https://baijiahao.baidu.com/s?id=1764400201507671499&wfr=spider&for=pc