数据倾斜原因及处理
一、原因
1)、key分布不均匀
2)、业务数据本身的特性
3)、建表时考虑不周
4)、某些SQL语句本身就有数据倾斜
操作 | 情形 |
---|---|
group by | group by 维度过小,某值的数量过多 |
Count Distinct | 某特殊值过多 |
Join | 大表join小表,其中小表key集中,分发到某一个或几个reduce上的数据远高于平均值 |
二、数据倾斜的解决方案
2.1参数调节(group by造成数据倾斜)
set hive.map.aggr=true 设置在Map端进行聚合
set hive.groupby.skewindata = true 实现方法是在group by时启动两个MR job。第一个job会将map端数据随机输入reducer,每个reducer做部分聚合,相同的key就会分布在不同的reducer中。第二个job再将前面预处理过的数据按key聚合并输出结果,这样就起到了均衡的效果
select word,count(word) from wordtest group by wordtest
案例:统计单词数量,假设单词d数据倾斜
with
dt as (select word, num, row_number () over () rn from wordtest where word='d'),
dd as (select rn%3 t, sum (num) total from dt group by rn%3),
df as (select 'd' word, sum (total) total from dd),
dw as (select word , sum(num) total from wordtest where word<>'d' group by word)select * from df union all select * from dw;
2.2 Sql语句优化
如果数据量非常大,执行如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;
2.3空值产生的数据倾斜:
解决方法1:空值不参与关联
select * from log a
join 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 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;
结论:方法2比方法1效率更好,解决方法1中 log读取两次,jobs是2。解决方法2 job数是1 。这个优化适合无效 id 产生的倾斜问题。把空值的 key 变成一个字符串加上随机数,就能把倾斜的数据分到不同的reduce上 ,解决数据倾斜问题。
2.4不同数据类型关联产生数据倾斜
默认的Hash操作会按int型的id来进行分配,这样会导致所有string类型id的记录都分配到一个Reducer中
select * from users a left outer join logs b
on a.usr_id = cast(b.user_id as string)
2.5大表join小表
map join 概念:将其中做连接的小表(全量数据)分发到所有 MapTask 端进行 Join,从 而避免了 reduceTask,前提要求是内存足以装下该全量数据
set hive.auto.convert.join= true
set hive.mapjoin.smalltable.filesize`,当小表小于该值就会启用map join,默认值25000000(25MB)
2.6大表join大表
解决办法1:参数设置
set hive.optmize.skewjoin=true
set hive.skewjoin.key=100000; #默认为100000
hive在运行的时候没有办法判断哪个key会产生多大的倾斜,所以使用这个参数控制倾斜的阀值,如果超过这个值,新的值会发送给那些还没有达到的reduce,一般可以设置成你处理的总记录数/reduce个数的2-4倍都可以接受
解决办法2:从其中一个大表中提取join字段形成小表,再和大表进行join
select * from log a
left outer join (
select d.*
from ( select distinct user_id from log ) c
join users d
on c.user_id = d.user_id
) x
on a.user_id = b.user_id;
转载链接: https://blog.csdn.net/mqd_chan/article/details/114271037.