数据倾斜原因及其处理方式

前言
本文是介绍的是开发spark极其核心的地方,可以说懂得解决spark数据倾斜是区分一个spark工程师是否足够专业的标准,在面试

中以及实际开发中,几乎天天面临的都是这个问题。

原理以及现象
先来解释一下,出现什么现象的时候我们认定他为数据倾斜,以及他数据倾斜发生的原理是什么?

比如一个spark任务中,绝多数task任务运行速度很快,但是就是有那么几个task任务运行极其缓慢,慢慢的可能就接着报内存溢出的问题了,那么这个时候我们就可以认定他是数据倾斜了。

接下来说一下发生数据倾斜的底层理论,其实可以非常肯定的说,数据倾斜就是发生在shuffle类的算子中,在进行shuffle的时候,必须将各个节点的相同的key拉到某个节点上的一个task来进行处理,比如按照key进行聚合和join操作等,这个时候其中某一个key数量量特别大,于是就发生了数据倾斜了。

 

数据倾斜示意图

分组聚合

分组聚合逻辑中,需要把相同key的数据发往下游同一个task,如果某个或某几个key的数量特别大,则会导致下游的某个或某几个task所要处理的数据量特别大,也就是要处理的任务负载特别大

 

JOIN计算

join计算中,A表和B表中相同key的数据,需要发往下游同一个task,如果A表中或B表中,某个key或某几个key的数量特别大,则会导致下游的某个或某几个task所要处理的数据量特别大,也就是要处理的任务负载特别大

定位数据倾斜的代码

上面我们知道了数据倾斜的底层原理,那么就好定位代码了,所以我就可以改写这段代码,让spark任务来正常运行了。

我们知道了导致数据倾斜的问题就是shuffle算子,所以我们先去找到代码中的shuffle的算子,比如distinct、groupBYkey、reduceBykey、aggergateBykey、join、cogroup、repartition等,那么问题一定就出现在这里。

找到shuffle类的算子之后,我们知道一个application分为job,那么一个job又划分为多个stage,stage的划分就是根据shuffle类的算子,也可以说是宽依赖来划分的,所以这个时候我们在spark UI界面上点击查看stage,如下图
 

 

可以看到94这一行和91这一行,执行时间明显比其他的执行时间要长太多了,我们就可以肯定一定是这里发生了数据倾斜,然后我们就找到了发生数据倾斜的stage了,然后根据stage划分原理,我们就可以推算出来发生倾斜的那个stage对应的代码中的哪一部分了。

这个时候我们找到了数据倾斜发生的地方了,但是我们还需要知道到底是哪个key数据量特别大导致的数据倾斜,于是接下来来聊一聊这个问题。

找到这个key的算法,我们可以使用采样的方式,对,就是当初虐了我们千百遍的概率论与数理统计的课上讲的采样算法。

代码如下:
 

val sampledPairs = pairs.sample(false, 0.1)
val sampledWordCounts = sampledPairs.countByKey()
sampledWordCounts.foreach(println(_))

现在我来简单说一下他的原理,他就是从所有key中,把其中每一个key随机取出来一部分,然后进行一个百分比的推算,学过采样算法的都知道,这是用局部取推算整体,虽然有点不准确,但是在整体概率上来说,我们只需要大概之久可以定位那个最多的key了

解决数据倾斜的方案

 

解决方案一: 提高shuffle 的并行度。
他的原理很简单,我们知道在rduceBykey中有一个shuffle read task的值默认为200,也就是说用两百个task来处理任务,对于我们一个很大的集群来说,每个task的任务中需要处理的key也是比较多的,这个时候我们把这个数量给提高以爱,比如我么设置reduceBYkey(1000),这个时候task的数量就多了,然后分配到每个task中的key就少了,于是说并行度就提高了。但是总体来说,这种解决办法对于某一个数量特别大的key来说效果甚为,只能说key多的时候,我们可以有一定的程度上环境数据倾斜的问题,所以这种方法也不是我们要找到的最好的办法,他也是有一定的局限性。

 

 

 

 

### Hadoop 数据倾斜原因 数据倾斜指的是大量相同的 key 被 partition 函数分配到了同一个分区内,在 MapReduce 编程模型中非常普遍。这种现象违背了并行计算的原则,因为这会造成某些节点承担过多的工作负荷,而其他节点则处于闲置状态[^2]。 具体而言,当某个特定键的数量远超于其它键时,该键对应的任务会处理异常庞大的数据集,使得其所在 Reduce 任务所需时间和资源显著增加。与此同时,其余 Reduce 任务可能已经完成工作,但由于存在未完成的大负载任务,整个作业无法结束,进而影响整体性能和效率[^4]。 #### 表现形式 - 单个或少数几个 Reducer 进度缓慢甚至停滞不前; - 各种 Container 报告内存溢出错误 (OOM); - 某些阶段读取/写出的数据量明显高于正常水平; - 部分 Task 可能会被强制终止以防止长时间占用集群资源[^3]。 ### 解决策略 针对上述情况,可以采取多种措施来缓解乃至消除数据倾斜带来的负面影响: 1. **优化输入数据分布** - 对原始数据源进行预处理,通过调整记录顺序等方式减少极端值的影响;也可以考虑引入随机因子打乱原本有序排列的数据流。 2. **自定义 Partitioner 类** ```java public class CustomPartitioner extends Partitioner<Text, IntWritable> { @Override public int getPartition(Text key, IntWritable value, int numPartitions) { // 实现更合理的哈希算法或其他逻辑确保不同 Key 的均匀散列 return Math.abs(key.hashCode()) % numPartitions; } } ``` 3. **采用 Combiner 或者 In-Mapper Combining 技术** 在 Mapper 中提前聚合部分中间结果,降低传输至 Reducer 层面的数据总量,减轻后者负担的同时提高网络带宽利用率。 4. **设置合适的参数配置** - `mapreduce.job.reduces` 参数控制 Reduce 数目,适当增大此数值有助于分散热点区域的压力; - 使用压缩编码方式保存临时文件可有效节省磁盘空间与 I/O 开销; - 根据实际需求动态调整 JVM 堆大小(`Xmx`)等硬件资源配置选项也能改善运行状况。 5. **应用 Skew Join 算法** 当遇到 join 操作中的严重偏斜情形时,可以通过构建辅助表、拆分子查询等形式重构 SQL 语句结构,实现更加高效稳定的连接操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值