前言
对于一个算法工程师来说,Spark是一个非常常用的工具。对于使用过Spark的同学,对于数据倾斜的问题一定不会陌生。
本文讲主要介绍Spark中的数据倾斜以及一些处理方案。
啥是数据倾斜?
这一切可能要从分布式计算系统说起,用最通俗的话来说,现在我们有一个任务,在一台电脑上跑要100个小时,于是我们灵光一现,我们整一百台不就行了?
于是,我们得到 任务处理时间 = 100小时/100台电脑 = 1小时
以上,就是一个理想的分布式系统。
然而,在现实中,我们需要考虑一个问题,我们这个100小时的任务能否被完美的分成100个1小时的任务?
现实中的问题很复杂,这次,有一个2小时的任务不能被分解,所以,我们这一百台电脑的运行时间变成了2小时,比理想状况增加了一倍的时间。
其实,刚刚在前面描述的问题和数据倾斜很相似,数据倾斜是指在分布式系统中,机器之间任务分配不均匀,导致大部分机器完成任务后等待个别机器的现象,这种现象造成的时间性能和计算资源的浪费,所以,我们要想方设法的缓解和消除数据倾斜。
那咋消除数据倾斜呢?
关于数据源倾斜
如果我们Spark任务的数据源发生了倾斜,这种时候我们可以通过调整partition的大小来规避数据倾斜。
在读取输入文件时,我们使用textFile(path, minPartitions)来读文件,下面的代码说明了一个Partition分区大小是如何计算的。
其中,minSize默认为1B,goalSize = 总文件大小/minPartitions,blockSize为HDFS默认的分片大小。
protected long computeSplitSize(long goalSize, long minSize, long blockSize) {
return Math.max(minSize, Math.min(goalSize, blockSize));
}
当数据源是可以切分的时候,我们可以通过调整minPartitions来控制读入分片大小,从而消除数据倾斜。
关于Reduce数据倾斜
在Reduce侧,如果并行度设置的不均衡,可能会导致task分配不均,这种情况可以增加并行度,分散之前task集中的reduce。
关于自定义partitioner
我们可以通过自定义partitioner,按照我们的需求把数据均匀的分配到不同的机器上,以解决数据倾斜。
.groupByKey(new Partitioner() {
@Override
public int numPartitions() {
return 14;
}
@Override
public int getPartition(Object key) {
int id = Integer.parseInt(key.toString());
if(id >= 9500000 && id <= 9500084 && ((id - 9500000) % 14) == 0) {
return (id - 9500000) / 14;
} else {
return id % 14;
}
}
})
最后
本文总结了几种常用的缓解数据倾斜的方法,由于本身不是专门做数据方面的同学,可能有一些疏忽和纰漏,欢迎大家指点和交流。