数据倾斜解决方案之原理以及现象分析

本文深入探讨大数据处理中常见的数据倾斜问题,分析其原因及表现形式,并提供多种解决方案,包括聚合源数据、过滤倾斜key、提高reduce并行度、双重聚合、mapjoin、sample采样与随机数扩容等,旨在帮助读者有效解决数据倾斜带来的性能瓶颈。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、什么是数据倾斜

数据倾斜是大数据类型项目中最棘手的性能问题。数据倾斜一般会有两种表现:

  1. 大部分task执行很快,只有极少数的task执行得特别慢,可能耗费数个小时。

  2. 大部分task很快执行完,但有的task会突然报OOM内存溢出,导致task fail、task lost、resubmitting task,反复执行几次,特定的task还是跑不通,最后挂掉。可能是因为这个task需要处理大量的数据,创建大量的对象,内存放不下,直接爆掉。

 

二、原因分析

在执行shuffle操作的时候,比如说有3个task,100万数据。某一个key对应98万个values,分配到同一个task;另外两个task,每个task分配到1万个values,并且是对应不同的key。

这个时候,分别分配到1万个values的task很快执行完,但是分配到98万values的task执行起来特别慢,导致整个spark作业的运行时间特别长。

 

三、定位原因与出现问题的位置

出现数据倾斜,基本只可能是因为发生了shuffle操作,在shuffle的过程中,某个key对应的数据远远多于其他key对应的数据,导致数据倾斜。这种情况下,可以通过两种方式去定位:

  1. 到程序中去找,哪些地方用了会产生shuffle的算子,比如groupByKey、countByKey、reduceByKey、join
  2. 看log文件,一般会报程序的哪一行代码导致了OOM异常;或者执行到了第几个stage

 

下面开始介绍几个数据倾斜的解决方案:

四、聚合源数据和过滤导致数据倾斜的key

①聚合源数据

通常,在做一些聚合操作时,比如groupByKey、reduceByKey、join,拿到的源数据都是每个key对应的values,然后在spark作业中对数据进行一定的操作。

如果这些数据来自hive表,可以在hive表中对数据进行ETL,对数据进行一定的处理。由于hive比较适合做离线分析,可以做定时任务,定时对数据进行聚合(也可能是初步计算)。如果按key来分组,将key对应的所有values,用特定的方式拼接到一个字符串中去,比如“key=sessionid,value:action_seq=1|user_id=1|search_keyword=电子|category_id=01;action_seq=2|user_id=1|search_keyword=书籍|category_id=01”。

在hive表中对key进行聚合后,当需要对key进行操作时,spark中通过key拿到的数据就是hive表聚合过的数据。可能hive表进行聚合前是10万条数据,聚合后就变成了1千条数据,解决了数据倾斜的问题。

②过滤导致数据倾斜的key

对于待处理数据,可能大多数key对应的数据都是几十、几百条,只有极少数key对应的数据是数十万条。

这个时候,根据业务需求,如果允许的情况下,可以将那极少数的key对应的数据舍弃掉,避免数据倾斜。

如果上面两个方法可以采纳,那么基本上可以从源头上解决绝大部分数据倾斜问题。实在不行,再尝试下面几个方法。

 

五、提高shuffle操作reduce并行度

提高reduce并行度,就是提高task的数量。

举个例子,原本某个task分配数据特别多,直接OOM(内存溢出),导致程序无法运行。根据log,找到发生数据倾斜的shuffle操作,提高并行度(在算子后面传入一个参数,即可设置并行度,比如reduceByKey(xxx,1000)),让数据尽量均匀地分配到所有的task中。每个task分配到的数据量变少,避免OOM,缓解数据倾斜的问题。

注意:

这种方法并没有从根本上解决数据倾斜的问题,只是缓解了数据倾斜带来的影响。

在理想状态下,如果这步操作之后,数据倾斜的问题得到解决,最好;如果对数据倾斜问题的影响很小,则不考虑这种方法,参考其他方法。

 

六、使用随机key实现双重聚合

在原始key前面加一个随机数(不同场景灵活应对),生成双重key,然后进行第一轮聚合;第一轮聚合之后,再将双重key反向映射成原始key,进行第二轮聚合。

在groupByKey、reduceByKey等算子中,可以采取这个方法。

如果前面的几种方法(聚合源数据和过滤导致数据倾斜的key、提高shuffle操作reduce并行度)无法解决数据倾斜问题,则只能依靠这种方法。

 

七、将reduce join转换为map join

普通join,需要经过shuffle操作,是reduce join,先将所有相同key对应的values汇聚到一个task中,然后再进行join。

map join,把小的RDD做成广播变量,再进行map操作,得到聚合后的RDD,最后再进行后续操作。

①适用场景

如果两个RDD要进行join,其中一个RDD比较小(比如一个RDD是100万数据,另一个RDD是1万数据),可以采取这种方法。广播出去的RDD,在每个executor的block manager上都会有一个数据备份,所以采取这种方法的时候,要确保内存足够存放小RDD的数据。

这个方式下,不会发生shuffle操作,从根本上杜绝了数据倾斜的问题。

②不使用场景

两个RDD都比较大,如果将一个RDD广播出去,每个executor上都会有一个数据备份,可能会导致OOM(内存溢出)。

③总结

对于join这种操作,即使没有数据倾斜,在条件允许的情况下,也优先使用map join方法,避免通过shuffle进行join,牺牲少量的内存来换取更好的性能。

 

八、sample采样倾斜key进行两次join

将发生数据倾斜的key单独去出来,放到一个RDD中,将原始RDD拆分成两个RDD:导致倾斜的key的RDD和其他RDD。用这两个RDD单独join,此时key对应的数据会分散到多个task中进行join操作,避免数据倾斜造成的问题,然后将两次join的结果执行union操作合并起来。

①适用场景

对于join操作,优先使用map join替代普通join。如果不能采用map join,并且某一个或几个key对应的数据量比较大,考虑使用sample采样。

②不适用场景

如果一个RDD中,导致数据倾斜的key特别多,此时不建议使用sample采样的方法,建议考虑下面第九种方法(sample采样倾斜key进行两次join)。

③优化

对于采样的key,从另一个要join的表中也过滤出一份数据,比如就只有一条数据。对这一个数据,进行flatMap操作,打上100个随机数作为前缀,返回100条数据。

单独拉出来的可能产生数据倾斜的RDD,给每一个数据都带上一个100以内的随机数最为前缀。

然后在进行join,整个过程会被打散到不同的task中运行,性能会提高很多。最后,再将结果union合并。

 

九、使用随机数以及扩容表进行join

当前面几个方法都没法使用时,可以尝试使用随机数以及扩容表进行join。这个方法没有办法彻底解决数据倾斜,只是一种对数据倾斜的缓解。

步骤:

  1. 选择一个RDD,用flatMap进行扩容,将一条数据映射成多条数据,每一条映射出来的数据都到一个n以内的随机数,比如说n取10。
  2. 将另一个RDD,做普通的map映射,每条数据,前面带上一个n以内的随机数。
  3. 将两个处理后的RDD进行join操作,并将结果数据反映射,去掉前面的随机数。

局限性:

  1. 两个RDD都很大,所以没有办法将某一个RDD扩得特别大,一般取10。
  2. 扩容的倍数比较小,没法彻底解决数据倾斜的问题,只能缓解数据倾斜带来的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值