Spark RDD - 避免shuffle - 分区是否有助于处理大文件?

时间:2016-05-29 18:18:55

标签: apache-spark spark-dataframe

我有一个大约10个平面文件的应用程序,每个文件的价值超过200MM +记录。业务逻辑涉及按顺序连接所有这些逻辑。

我的环境: 1个主站 - 3个从站(用于测试我为每个节点分配了1GB内存)

大多数代码只对每个联接执行以下操作

 RDD1 = sc.textFile(file1).mapToPair(..)

 RDD2 = sc.textFile(file2).mapToPair(..) 

 join = RDD1.join(RDD2).map(peopleObject)

任何调整建议,如重新分区,并行化......?如果是这样,有什么最好的做法可以为重新分配提供良好的数字?

使用当前配置,作业需要一个多小时,我看到几乎每个文件的随机写入都是> 3GB

2 个答案:

答案 0 :(得分:3)

实际上,对于大型数据集(每个5,100G +),我发现在您开始加入一系列连接之前对所有RDD进行共同分区时,连接效果最佳。

RDD1 = sc.textFile(file1).mapToPair(..).partitionBy(new HashPartitioner(2048))

RDD2 = sc.textFile(file2).mapToPair(..).partitionBy(new HashPartitioner(2048)) 
.
.
.
RDDN = sc.textFile(fileN).mapToPair(..).partitionBy(new HashPartitioner(2048)) 

//start joins

RDD1.join(RDD2)...join(RDDN)

<小时/> 边注: 我将这种连接称为树连接(每个RDD使用一次)。理论基础在以下来自Spark-UI的精美图片中展示:

enter image description here

答案 1 :(得分:2)

如果我们总是加入一个RDD(比如说rdd1)和其他所有RDD,那么我们的想法就是将RDD分区然后再保留它。

这是sudo-Scala实现(可以很容易地转换为Python或Java):

val rdd1 = sc.textFile(file1).mapToPair(..).partitionBy(new HashPartitioner(200)).cache()

到此为止,我们将rdd1分成200个分区。它将第一次被评估,它将被持久化(缓存)。

现在让我们阅读另外两个rdds并加入它们。

val rdd2 = sc.textFile(file2).mapToPair(..) 
val join1 = rdd1.join(rdd2).map(peopleObject)
val rdd3 = sc.textFile(file3).mapToPair(..) 
val join2 = rdd1.join(rdd3).map(peopleObject)

请注意,对于重新生成的RDD,我们不对它们进行分区,也不对它们进行缓存。

Spark会看到rdd1已经是哈希分区,它将为所有剩余的连接使用相同的分区。所以rdd2和rdd3将把它们的密钥改组到rdd1的密钥所在的相同位置。

为了更清楚,我们假设我们不进行分区,我们使用问题所示的相同代码;每次我们进行连接时,两个rdds都将被洗牌。这意味着如果我们有n个连接到rdd1,非分区版本将将rdd1洗牌N次。分区方法只会将rdd1洗牌一次。