hive数据倾斜及处理案例

什么是数据倾斜

数据倾斜其实是进行分布式计算的时候,某些节点的计算能力比较强或者需要计算的数据比较少,早早执行完了,某些节点计算的能力较差或者由于此节点需要计算的数据比较多,导致出现其他节点的reduce阶段任务执行完成,但是这种节点的数据处理任务还没有执行完成。

数据倾斜的现象

当我们在执行HiveQL或者运行MapReduce作业时候,如果遇到一直卡在map100%,reduce99%一般就是遇到了数据倾斜的问题。

hive数据倾斜的原因

1.空值产生的数据倾斜
2.不同数据类型关联产生的数据倾斜
3.关联的key非空,但是某个key值大量重复 #distinct、count(distinct)
4.某些SQL语句本身就有数据倾斜
5.不可拆分大文件引发的数据倾斜

在这里插入图片描述

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等。
	出现数据倾斜时, 可能就是你的代码中使用了这些算子中的某一个所导致的。

3.其他问题:

1.不同数据类型引发的数据倾斜:
	如果 key 字段既有 string 类型也有 int 类型,默认的 hash 就都会按 int 类型来 分配,那我们直接把 int 类型都转为 string 就好了,这样 key 字段都为 string, hash 时就按照 string 类型分配了。

2.不可拆分大文件引发的数据倾斜:
	只能将使用 GZIP 压缩等不支持文件 分割的文件转为 bzip 和 zip 等支持文件分割的压缩方式。所以,我们在对文件进行压缩时,为避免因不可拆分大文件而引发数据读取的倾斜,
	所以,我们在对文件进行压缩时,为避免因不可拆分大文件而引发数据读取的倾斜, 在数据压缩的时候可以采用 bzip2 和 Zip 等支持文件分割的压缩算法。

3.确实无法减少数据量引发的数据倾斜
	这类问题最直接的方式就是调整 reduce 所执行的内存大小。 调整 reduce 的内存大小使用	mapreduce.reduce.memory.mb 这个配置。

4.空值引发的数据倾斜
	第一种:可以直接不让 null 值参与 join 操作,即不让 null 值有 shuffle 阶段
	第二种:因为 null 值参与 shuffle 时的 hash 结果是一样的,那么我们可以给 null 值随机赋值,这样它们的 hash 结果就不一样,就会进到不同的 reduce 中

5.表连接时引发的数据倾斜
	通常做法是将倾斜的数据存到分布式缓存中,分发到各个 Map 任务所在节点。 在 Map 阶段完成 join 操作,即 MapJoin,这避免了 Shuffle,从而避免了数据倾斜。
	可以通过以下两个属性来设置该优化的触发时机:
		hive.auto.convert.join=true 默认值为 true,自动开启 MAPJOIN 优化。
		hive.mapjoin.smalltable.filesize=2500000 默认值为 2500000(25M),通过配置 该属性来确定使用该优化的表的大小,如果表的大小小于此值就会被加载进内存 中。

6.数据膨胀引发的数据倾斜
	在多维聚合计算时,如果进行分组聚合的字段过多,会导致 Map 端产出的数据急速膨胀,这种情况容易导致作业内存溢出的 异常。如果 log 表含有数据倾斜 key,会加剧 Shuffle 过程的数据倾斜。
	可以拆分sql。

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

以上是在工作和学习中遇到的数据倾斜的情况,也希望各位能够提供更多的建议,后续遇到会更新补充。

  • 4
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数据湖填坑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值