Spark数据倾斜问题

本文详细探讨了Spark数据倾斜的原因和危害,提供了针对源数据和计算过程的解决方法,包括聚合源数据、过滤倾斜key、提高shuffle操作并行度、使用随机key实现双重聚合、将reduce join转换为map join以及sample采样倾斜key单独进行join等策略,以缓解和避免数据倾斜问题。
摘要由CSDN通过智能技术生成

数据倾斜的危害

  1. spark中一个stage的执行时间受限于最后那个执行完的task,因此运行缓慢的任务会拖累整个程序的运行速度
  2. 过多的数据在同一个task中执行,将会把executor撑爆,造成OOM,程序终止运行

数据倾斜的原因

  1. 数据源数据文件不均匀(包括大量的key为空)
  2. 计算过程中key的分布不均
    1) 单个rdd中进行groupby 的时候key分布不均
    2)多个rdd进行join过程中key的不均匀

排查数据倾斜
【数据源文件】
  根据Spark UI查看metrics,input 以及shuffle read 两个metrics判断task的min跟max是否差异较大,如果差异非常大,则存在数据源倾斜。input size统计是从外部数据源读入的大小

【计算过程】
  根据Spark UI,查看该task的input或者shuffle reader的Summary Metrics里面min和max值,可以定位到task所属的stage,通过stage 描述可以定位到所属的代码行或者根据log一般会报是在哪一行代码,导致了OOM异常或者看log,看看是执行到了第几个stage。哪一个stage生成的task特别慢,就能够通过stage定位到你的代码,到底哪里发生了数据倾斜。

数据倾斜的常见解决方法

对于源数据

聚合源数据

适用场景
  如果Hive表中的数据很不均匀,而且业务场景需要频繁使用Spark对Hive表执行某个分析操作,比较适合使用这种技术方案。将数据倾斜提前到上游的Hive ETL,每天仅执行一次,只有那一次是比较慢的,而之后每次Java调用Spark作业时,执行速度都会很快,能够提供更好的用户体验。
实现思路
  直接在生成hive表的hive etl中对数据进行聚合。比如按key来分组,将key对应的所有的values全部用一种特殊的格式拼接到一个字符串里面去,比如“key=sessionid, value: action_seq=1|user_id=1|search_keyword=火锅|category_id=001;action_seq=2|user_id=1|search_key
word=涮肉|category_id=001”。
  对key进行group,在spark中,拿到key=sessionid,values< Iterable>。hive etl中,直接对key进行了聚合。那么也就意味着,每个key就只对应一条数据。在spark中,就不需要再去执行groupByKey+map这种操作了,就不需要执行shffule操作了,也就根本不可能导致数据倾斜。

  聚合源数据方案第二种做法是,如果没有办法对每个key聚合出来一条数据。对每个key对应的数据,10万条。有好几个粒度,比如10万条里面包含了几个城市、几天、几个地区的数据,现在放粗粒度。直接就按照城市粒度,做一下聚合,几个城市,几天、几个地区粒度的数据,都给聚合起来。比如说:
city_id date area_id
select … from … group by city_id

  尽量去聚合,减少每个key对应的数量,也许聚合到比较粗的粒度之后,原先有10万数据量的key,现在只有1万数据量。减轻数据倾斜的现象和问题。

过滤导致倾斜的key

适用场景
  如果发现导致倾斜的key就少数几个,而且对计算本身的影响并不大的话,可使用这种方案。
实现思路
  在Spark SQL中可以使用where子句过滤掉这些key或者在Spark Core中对RDD执行filter算子过滤掉这些key。如果需要每次作业执行时,动态判定哪些key的数据量最多然后再进行过滤,那么可以使用sample算子对RDD进行采样,然后计算出每个key的数量,取数据量最多的key过滤掉即可。

对于计算过程

提高shuffle操作reduce并行度

  对所有的shuffle算子,如groupByKey、countByKey、reduceByKey。在调用的时候,传入进去一个参数。那个数字,就代表了那个shuffle操作的reduce端的并行度。增加reduce task的数量,让每个reduce task分配到更少的数据量。这样的话也许就可以缓解甚至是基本解决掉数据倾斜的问题。

使用随机key实现双重聚合

适用场景:对于groupByKey、reduceByKey造成的数据倾斜,有比较好的效果。
实现思路
  第一轮局部聚合,对key进行打散,将原先一样的key,变成不一样的key,相当于是将每个key分为多组,先针对多个组,进行key的局部聚合。第二轮全局聚合,再去除掉每个key的前缀,然后对所有的key进行全局的聚合。

// 第一步,给RDD中的每个key都打上一个随机前缀。
JavaPairRDD<String, Long> randomPrefixRdd = rdd.mapToPair(
        new PairFunction<Tuple2<Long,Long>, String, Long>() {
   
private static final long serialVersionUID = 1L;
@Override
public Tuple2<String, Long> call(Tuple2<Long, Long> tuple)
                    throws Exception {
   
                Random random = new Random();
                int prefix = random.nextInt(10);
return new Tuple2<String, Long>(prefix + "_" + tuple._1, tuple._2);
            }
        });

// 第二步,对打上随机前缀的key进行局部聚合。
JavaPairRDD<String
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值