什么是数据倾斜
数据倾斜是指我们在并行进行数据处理的时候,由于数据Spark的单个Partition)的分布不均,导致大量的数据集中分不到一台或者某几台计算节点上,导致处理速度远低于平均计算速度,从而拖延导致整个计算过程过慢,影响整个计算性能
数据倾斜的危害
- 单个或者某几个task拖延整个任务运行时间,导致整体耗时过大
- 单个task处理数据过多,很容易导致oom
- Executor Kill lost,Shuffle error
数据倾斜的产生
数据倾斜容易产生在两个过程,本身数据源读的倾斜,这个主要由于本身文件的分布不均,主要是不能切分的文件isSplitable=false 例如gz 另外的在shuffle阶段,key的分布不均,导致大量的数据集中到单个或者某几个task上导致数据整个stage,执行慢,影响整个job作业,总结主要有以下两个过程
- 数据源数据文件不均匀
- 计算过程中key的分布不均
- 单个rdd中进行groupby 的时候key分布不均
- 多个rdd进行join过程中key的不均匀
数据倾斜快速定位
1.我们可以根据Spark UI查看metrics,input 以及shuffle read 两个metrics判断task的min,跟max是否差异较大,如果差异非常大,并且影响运行,则需要优化task input 数据源倾斜,input size统计是从外部数据源读入的大小

2.task shuffle 数据倾斜,一般主要是shuffle read拉取数据的时候,数据partition分布不均,导致fetch拉取过程中数据倾斜,可以通过Shuffle Read Size查看min,和max 值,如果差异非常大,并且影响运行,则需要优化

3.另外就是我们在运行中个别task执行特别慢的时候,我们可以看一下该task的input或者shuffle reader的Summary Metrics里面min和max值,一般情况下处理的数据越多,task的运行时间越长,理想情况下所有的task数据均匀分布,运行时长均等,可以定位到task所属的stage,通过stage 描述,可以定位到所属的代码行,进而优化代码

数据倾斜的常见解决方法
-
数据源数据文件不均匀
-
原理:
对于spark读取文件主要通过sparkContext.textFile调用hadoop的TextInputFormat读取文件,然后实现两个方法isSplitable和getSplits,isSplitable判断文件是否切分,getSplits是切分文件生成partition,每个partition对应一个rdd task,blocksize 的计算如下,切分的partition数量=goalSize/splitSize,运行任务的task的数量等于依赖的切分的partition数量
//默认blocksize为256M, minSize 默认1, Math.min(goalSize, blockSize) 计算文件的goalSize,如果文件goalSize小于blocksize则取goalSize,否则取blocksizeprotectedlongcomputeSplitSize(longgoalSize,longminSize,longblockSize) {returnMath.max(minSize, Math.min(goalSize, blockSize));}//根据总的goalSize/splitSize 如果小于1.1倍,则停止splitwhile(((double) bytesRemaining)/splitSize > SPLIT_SLOP) {String[][] splitHosts = getSplitHostsAndCachedHosts(blkLocations,length-bytesRemaining, splitSize, clusterMap);splits.add(makeSplit(path, length-bytesRemaining, splitSize,splitHosts[0], splitHosts[1]));bytesRemaining -= splitSize;} - 案例:
分别对于不能split的gz文件和可以split的文本文件进行计数统计,对于不能split的gz文件,spark只能启动一个task进行计数统计,对于可以split的文本文件,spark按照goalSize/splitSize切分文本生成多个task进行并行读取- 对于不能split的gz文件进行读取,只能按照 文件数量生成task进行计算
-
使用spark 简单的对gz文件进行读取统计行数
valspark=SparkSession.builder().appName("spark_read").getOrCreate();spark.sparkContext.textFile("/user/xxx/example/gzip/lineitem.tbl.gz").count()spark.close() -
提交spark app 运行情况,按照文件数量,只有一个文件生成一个task进行计算

-
- 对于可以使用split的文件进行读取,任务可以被按照blocksize进行切分,进行并行计算
- 使用spark 简单的对gz文件进行读取统计行数
-
文件信息统计信息如下, task数量 = (total size:11811160064)/(block size:268435456) 为44个task,进行并行计算
-
提交spark app 运行情况,按照block 数量并行生成44个task进行计算

- 使用spark 简单的对gz文件进行读取统计行数
- 对于不能split的gz文件进行读取,只能按照 文件数量生成task进行计算
-
总结:
适用场景:对于数据源单个spark input read数据量过大,或者单个task 相对于其他task spark input read较大的情况,读取数据源明显不均匀
解决方式:尽量使用可切割的文本存储,生成尽量多的task进行并行计算
优点:从数据源避免倾斜,并且从源头增大并行度,避免倾斜
缺点:需要改造数据源,支持可切割
-
-
Shuffle过程中数据分布不均
- 原理:
Shuffle阶段在分布式并行计算引擎中是常见一个过程,在spark中当一个RDD的数据需要被多个子RDD所使用的时候,我们需要进行shuffle将数据打散,把数据均匀的分配给子RDD进行并行计算,Shuffle过程中spark默认使用HashPartitioner对数据进行分区,在这个过程中可能由于我们的数据分布不均,我们在进行hash取摸的时候,并行度设置不足,导致多数据分配到一个task上,导致倾斜,或者就是相同key的数据hash取摸之后就是比较大,分配同一个task导致数据倾斜等,对于这行情况我们分以下场景进行解决 - 案例1:shuffle中部分数据分布不均
spark shuffle默认使用HashPartitioner对数据进行分片,可能造成不同的key分配到一个task上,导致数据倾斜-
spark 生成倾斜数据并提交任务,生成100w的数据,然后设置默认spark.default.parallelism并行的task为100,倾斜的分区为7,对大于100的数据,随按照new Random()).nextInt(defPar) * (skewPart)生成key,使key hash取摸的时候,都分配分区为7的task上,导致数据倾斜
valnumbers=1000
-
- 原理:

本文详细探讨了Spark中的数据倾斜问题,包括其定义、危害和产生原因。介绍了如何通过Spark UI快速定位数据倾斜,并提出了数据源不均匀、shuffle过程中数据分布不均等常见场景的解决方案,如调整并行度、自定义分区、使用广播join和采样处理倾斜键等方法,以优化计算性能。

524

被折叠的 条评论
为什么被折叠?



