什么是数据倾斜
数据倾斜其实是进行分布式计算的时候,某些节点的计算能力比较强或者需要计算的数据比较少,早早执行完了,某些节点计算的能力较差或者由于此节点需要计算的数据比较多,导致出现其他节点的reduce阶段任务执行完成,但是这种节点的数据处理任务还没有执行完成。
数据倾斜的现象
当我们在执行HiveQL或者运行MapReduce作业时候,如果遇到一直卡在map100%,reduce99%一般就是遇到了数据倾斜的问题。
hive数据倾斜的原因
1.空值产生的数据倾斜
2.不同数据类型关联产生的数据倾斜
3.关联的key非空,但是某个key值大量重复 #distinct、count(distinct)
4.某些SQL语句本身就有数据倾斜
hive数据倾斜的解决方法
1.参数调节
set hive.map.aggr=true 这个配置项代表是否在map端进行聚合
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 中),最后完成最终的聚合操作。
set hive.merge.mapfiles=true 当出现小文件过多,需要合并小文件。
2.sql语句优化
1、使用分区剪裁、列剪裁
在分区剪裁中,当使用外关联时,如果将副表的过滤条件写在Where后面,那么就会先全表关联,之后再过滤。
2、尽量不要用COUNT DISTINCT,因为COUNT DISTINCT操作需要用一个Reduce Task来完成,这一个
Reduce需要处理的数据量太大,就会导致整个Job很难完成,一般COUNT DISTINCT使用先GROUP BY再
COUNT的方式替换,虽然会多用一个Job来完成,但在数据量大的情况下,这个绝对是值得的。
3、使用with as,因为拖慢hive查询效率出了join产生的shuffle以外,还有一个就是子查询,在SQL语句里
面尽量减少子查询。with as是将语句中用到的子查询事先提取出来(类似临时表),使整个查询当中的所
有模块都可以调用该查询结果。使用with as可以避免Hive对不同部分的相同子查询进行重复计算。
4.1、大小表的join
写有Join操作的查询语句时有一条原则:应该将条目少的表/子查询放在Join操作符的左边。原因是在
Join操作的Reduce阶段,位于Join操作符左边的表的内容会被加载进内存,将条目少的表放在左边,可以
有效减少发生OOM错误的几率。
但新版的hive已经对小表JOIN大表和大表JOIN小表进行了优化。小表放在左边和右边已经没有明显区别。
不过在做join的过程中通过小表在前可以适当的减少数据量,提高效率。
4.2、大表Join大表
遇到需要进行join的但是关联字段有数据为null,如表一的id需要和表二的id进行关联,null值的reduce就
会落到一个节点上.
解决方法1:子查询中过滤掉null值,id为空的不参与关联
解决方法2:用case when给空值分配随机的key值(字符串+rand())
5、数据倾斜,数据倾斜的原理都知道,就是某一个或几个key占据了整个数据的90%,这样整个任务的效
率都会被这个key的处理拖慢,同时也可能会因为相同的key会聚合到一起造成内存溢出。数据倾斜只会发
生在shuffle过程中。
这里给大家罗列一些常用的并且可能会触发shuffle操作的算子:distinct、 groupByKey、reduceByKey、
aggregateByKey、join、cogroup、repartition等。
出现数据倾斜时, 可能就是你的代码中使用了这些算子中的某一个所导致的。
hive数据倾斜的处理案例
1、 空值产生的数据倾斜场景: 如日志中,常会有信息丢失的问题,比如全网日志中的user_id,如果取其中的user_id和bmw_users关联,会碰到数据倾斜的问题。
解决方法1:过滤null值,user_id为空的不参与关联
select * from log a join bmw_users b on a.user_id is not null and a.user_id = b.user_id
union all
select * from log a where a.user_id is null;
解决方法2 :赋与空值分新的key值(推荐)
select * from logs a left join bmw_users b
on
case when a.user_id is null
then
concat(‘dp_hive’,rand())
else
a.user_id end = b.user_id;
正常的方案是null进行过滤,但是日常情况下不是这中特殊的key。 那么在日常需求的情况下如何处理 这种数据倾斜的情况呢:
1、sample采样,获取哪些集中的key;
2、将集中的key按照一定规则添加随机数;
3、进行join,由于打散了,所以数据倾斜避免了;
4、在处理结果中对之前的添加的随机数进行切分,变成原始的数据;
2、关联的key非空,但是某个key值大量重复 解决方法:加入随机数
select key, round(rand()*1000) as rnd,
3、 不同数据类型关联产生数据倾斜场景: 用户表中user_id字段为int,logs表中user_id字段既有string类型也有int类型。当按照user_id进行两个表的join操作时,默认的Hash操作会按照int型的id来进行分配,这样会导致所有string类型的id记录都分配到同一个reduce中。 解决方法:把数字类型转换成字符串类型
select * from users a left join logs b on a.user_id = cast(b.user_id as string)
4、distinct、count(distinct) 解决方法:用group by 去重
select count(distinct uid)
from test
where ds='2020-08-10' and uid is not null
转换为
select count(a.uid)
from
(select uid from test where uid is not null and ds = '2020-08-10' group by uid)
5、with as是将语句中用到的子查询事先提取出来(类似临时表),使整个查询当中的所有模块都可以调用该查询结果。使用with as可以避免Hive对不同部分的相同子查询进行重复计算。
select a.*
from test1 a
left join test2 b on a.uid = b.uid
where a.ds='2020-08-10'
and b.ds='2020-08-10'
可以转化为
with b
as
select uid
from test2
where ds = '2020-08-10' and uid is not null
select a.*
from test1 a
left join b on a.uid = b.uid
where a.ds='2020-08-10' and a.uid is not null
以上是在工作和学习中遇到的数据倾斜的情况,也希望各位能够提供更多的建议,后续遇到会更新补充。
|