数据倾斜
如果有10亿数据,一台电脑可能要10小时,现在集群有10台,可能1小时就够了,但是有可能大量的数据集中到一台或几台上,要5小时,发生了数据倾斜,例如:
公司一:总用户量1000万,5台64G内存的服务器。
公司二:总用户量10亿,1000台64G内存的服务器。
1.公司一的数据分析师在做join的时候发生了数据倾斜,会导致有几百万用户的相关数据集中到了一台服务器上,几百万的用户数据,说大也不大,正常字段量的数据的话64G还是能轻松处理掉的。
2.公司二的数据分析师在做join的时候也发生了数据倾斜,可能会有1个亿的用户相关数据集中到了一台机器上了(相信我,这很常见)。这时候一台机器就很难搞定了,最后会很难算出结果。
1.数据倾斜表现
1.1 hadoop中的数据倾斜表现:
- 有一个多几个Reduce卡住,卡在99.99%,一直不能结束。
- 各种container报错OOM
- 异常的Reducer读写的数据量极大,至少远远超过其它正常的Reducer
- 伴随着数据倾斜,会出现任务被kill等各种诡异的表现。
1.2 hive中数据倾斜
任务进度长时间维持在 99%或者 100%的附近,查看任务监控页面,发现只有少量 reduce子任务未完成,因为其处理的数据量和其他的 reduce 差异过大。单一 reduce 处理的记录数和平均记录数相差太大,通常达到好几倍之多,最长时间远大于平均时长。
一般都发生在Sql中group by和join on上,而且和数据逻辑绑定比较深。
1.3 Spark中的数据倾斜
Spark中的数据倾斜,包括Spark Streaming和Spark Sql,表现主要有下面几种:
- Executor lost,OOM,Shuffle过程出错;
- Driver OOM;
- 单个Executor执行时间特别久,整体任务卡在某个阶段不能结束;
- 正常运行的任务突然失败;
2.数据倾斜产生原因
我们以Spark和Hive的使用场景为例。
他们在做数据运算的时候会涉及到,count distinct、group by、join on等操作,这些都会触发Shuffle动作。一旦触发Shuffle,所有相同key的值就会被拉到一个或几个Reducer节点上,容易发生单点计算问题,导致数据倾斜。
一般来说,数据倾斜原因有以下几方面:
2.1 key分布不均匀
2.2 建表时考虑不周
我们举一个例子,就说数据默认值的设计吧,假设我们有两张表:
user(用户信息表):userid,register_ip
ip(IP表):ip,register_user_cnt
这可能是两个不同的人开发的数据表。如果我们的数据规范不太完善的话,会出现一种情况:
user表中的register_ip字段,如果获取不到这个信息,我们默认为null;
但是在ip表中,我们在统计这个值的时候,为了方便,我们把获取不到ip的用户,统一认为他们的ip为0。
两边其实都没有错的,但是一旦我们做关联了,这个任务会在做关联的阶段,也就是sql的on的阶段卡死。
2.3业务数据本身的特, 业务数据激增
比如订单场景,我们在某一天在北京和上海两个城市多了强力的推广,结果可能是这两个城市的订单量增长了10000%,其余城市的数据量不变。
然后我们要统计不同城市的订单情况,这样,一做group操作,可能直接就数据倾斜了。
2.4 shuffle阶段出现数据倾斜
shuffle是按照key,来进行values的数据的输出、拉取和聚合的,一旦发生shuffle,所有相同key的值就会拉到一个或几个节点上,个别key对应的数据比较多,就容易发生单个节点处理数据量爆增的情况。
2.5key分布不均匀
存在大量相同值的数据
存在大量异常值或者空值
2.6某些SQL语句本身就有数据倾斜
- 两个表中关联字段存在大量空值(解决方法:去除或者加随机数),或是关联字段的数据不统一(解决方法:把数字类型转为字符串类型,统一大小写)
- join 一个key集中的小表 (解决方法:reduce join 改成 map join)
- group by维度过小 某值的数量过多 (解决方法:两阶段聚合,放粗粒度)
- count distinct 某特殊值过多 (解决方法:先用group by)
- 数据频率倾斜——某一个区域的数据量要远远大于其他区域。
- 数据大小倾斜——部分记录的大小远远大于平均值。
3.解决数据倾斜思路
以下解决思路引用自
https://blog.csdn.net/qq_30031221/article/details/114378730
3.1聚合类group by操作,发生数据倾斜
- map段部分聚合
- 开启Map端聚合参数设置set hive.map.aggr=true
- 在Map端进行聚合操作的条目数目set hive.grouby.mapaggr.checkinterval=100000
- 有数据倾斜的时候进行负载均衡(默认是false)set hive.groupby.skewindata = true
- 阶段拆分-两阶段聚合 需要聚合的key前加一个随机数的前后缀,这样就均匀了,之后再按照原始的key聚合一次
生- 成的查询计划有两 个 MapReduce 任务。在第一个 MapReduce 中,map 的输出结果集合会随机分布到 reduce 中, 每个 reduce 做部分聚合操作,并输出结果。相同的 Group By Key 有可 能分发到不同的 reduce 中,从而达到负载均衡的目的;第二个 MapReduce 任务再根据预处 理的数据结果按照 Group By Key 分布到 reduce 中(这个过程可以保证相同的 Group By Key 分布到同一个 reduce 中),最后完成最终的聚合操作。
假设 key = 水果
select count(substr(a.key,1,2)) as key
from(
select concat(key,'_',cast(round(10*rand())+1 as string)) tmp
from table
group by tmp
)a
group by key
3.2 空值产生的数据倾斜
1.在查询的时候,过滤掉所有为NULL的数据,比如:
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 log 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;
3.3 Reduce join 改为Map join
- 适用于小表和大表 join,将较小RDD中的数据直接通过collect算子拉取到Driver端的内存中来,然后对其创建一个Broadcast变量;接着对另外RDD执行map类算子,在算子函数内,从Broadcast变量中获取较小RDD 的全量数据,与当前RDD的每一条数据按照连接key进行比对,如果连接key相同的话,那么就将两个RDD的数据用你需要的方式连接起来。
- 设置自动选择MapJoin set hive.auto.convert.join = true;默认为true
- reduce join: 先将所有相同的key,对应的values,汇聚到一个task中,然后再进行join。
- map reduce:broadcast出去那个小表的数据以后,就会在每个executor的block manager中都驻留一份+map算子来实现与join同样的效果。不会发生shuffe,从根本上杜绝了join操作可能导致的数据倾斜的问题;
排序选择
- cluster by: 对同一字段分桶并排序,不能和sort by连用;
- distribute by + sort by: 分桶,保证同一字段值只存在一个结果文件当中,结合sort by 保证每个reduceTask结果有序;
- sort by: 单机排序,单个reduce结果有序
- order by:全局排序,缺陷是只能使用一个reduce