Spark-Repartition()与Coalesce()


252

根据Learning Spark

请记住,对数据重新分区是一项相当昂贵的操作。Spark还具有repartition()称为的优化版本,coalesce()可以避免数据移动,但前提是要减少RDD分区的数量。

我得到的一个区别是repartition()分区数可以增加/减少,但是coalesce()分区数只能减少。

如果分区分布在多台计算机上并coalesce()运行,那么如何避免数据移动?

Answers:


352

避免完全洗牌。如果知道数量正在减少,那么执行程序可以安全地将数据保留在最小数量的分区上,只需将数据从多余的节点移到我们保留的节点上即可。

因此,它将如下所示:

Node 1 = 1,2,3
Node 2 = 4,5,6
Node 3 = 7,8,9
Node 4 = 10,11,12

然后coalesce下降到2个分区:

Node 1 = 1,2,3 + (10,11,12)
Node 3 = 7,8,9 + (4,5,6)

请注意,节点1和节点3不需要移动其原始数据。


114
感谢您的回复。文档应该有更好的说法minimize data movement而不是avoiding data movement
Praveen Sripati 2015年

12
有什么情况repartition应该代替使用coalesce吗?
Niemand

20
@Niemand我认为当前文档对此进行了很好的介绍:github.com/apache/spark/blob/…请记住,所有repartition操作都是coalesce在将shuffle参数设置为true的情况下进行的。让我知道是否有帮助。
贾斯汀·皮洪尼

2
是否可以减少现有分区文件的数量?我没有hdfs,但文件很多。

2
从统计上讲,重新分区会变慢,因为它不知道收缩的幅度……尽管他们可以对其进行优化。在内部,它只是用一个shuffle = true标志来称呼合并
贾斯汀·皮洪尼

170

贾斯汀的答案太棒了,而且这种回应更加深入。

repartition算法会进行完整的混洗,并使用均匀分布的数据创建新分区。让我们创建一个数字框架,其数字从1到12。

val x = (1 to 12).toList
val numbersDf = x.toDF("number")

numbersDf 在我的机器上包含4个分区。

numbersDf.rdd.partitions.size // => 4

这是如何在分区上划分数据:

Partition 00000: 1, 2, 3
Partition 00001: 4, 5, 6
Partition 00002: 7, 8, 9
Partition 00003: 10, 11, 12

让我们对该repartition方法进行全面改组,并在两个节点上获取此数据。

val numbersDfR = numbersDf.repartition(2)

这是numbersDfR在我的机器上对数据进行分区的方式:

Partition A: 1, 3, 4, 6, 7, 9, 10, 12
Partition B: 2, 5, 8, 11

repartition方法将创建新分区,并将数据均匀分布在新分区中(对于较大的数据集,数据分布更加均匀)。

coalesce和之间的区别repartition

coalesce使用现有分区来最大程度地减少混洗的数据量。 repartition创建新的分区并进行完全随机播放。 coalesce导致分区的数据量不同(有时分区的大小差异很大),分区的大小repartition大致相等。

coalesce还是repartition更快?

coalesce可能比快repartition,但大小不等的分区通常比大小相同的分区要慢。过滤大型数据集后,通常需要重新分配数据集。我发现repartition总体上速度更快,因为Spark可以与相同大小的分区一起使用。

注意我好奇地发现分区会增加磁盘上的数据大小。在大型数据集上使用重新分区/合并时,请确保运行测试。

如果您想了解更多详细信息,请阅读此博客文章

在实践中何时使用合并和重新分区


8
很好的答案@Powers,但是分区A和分区B中的数据是否不正确?它如何均匀分布?
anwartheravian

另外,在没有OOM错误的情况下获取分区大小的最佳方法是什么。我使用,rdd.glom().map(len).collect()但是它给出了很多OOM错误。
anwartheravian

8
@anwartheravian-分区A和分区B的大小不同,因为该repartition算法对于很小的数据集不能平均分配数据。我曾经repartition将500万条记录组织到13个分区中,每个文件在89.3 MB和89.6 MB之间-相当相等!
Powers

1
@Powers这个看起来更好的详细答案。
绿色

1
这更好地解释了差异。谢谢!
阿比(Abhi)

22

这里要注意的另一点是,作为Spark RDD的基本原理是不变性。重新分区或合并将创建新的RDD。基本RDD将继续以其原始分区数量存在。如果用例需要将RDD保留在缓存中,则必须对新创建的RDD执行相同的操作。

scala> pairMrkt.repartition(10)
res16: org.apache.spark.rdd.RDD[(String, Array[String])] =MapPartitionsRDD[11] at repartition at <console>:26

scala> res16.partitions.length
res17: Int = 10

scala>  pairMrkt.partitions.length
res20: Int = 2

好一个!这是至关重要的,至少对于这个经验丰富的scala开发人员而言并不明显,即,无论是重新分区还是合并尝试修改数据,以及跨节点的分布方式都没有
doug

1
@Harikrishnan,因此,如果我正确理解了其他答案,那么在合并的情况下按照其回答,Spark使用现有分区,但是由于RDD是不可变的,您可以描述一下Coalesce如何利用现有分区吗?根据我的理解,我认为Spark将新分区追加到合并的现有分区中。
探索者

但是,如果执行图已知不再使用“旧的” RDD,则如果不保留,则会从内存中清除它,不是吗?
Markus

15

repartition -建议在增加分区数量的同时使用它,因为它涉及对所有数据进行混洗。

coalesce-建议在减少分区数的同时使用它。例如,如果您有3个分区,并且想要将其减少为2,coalesce则将第3个分区数据移动到分区1和2。分区1和2将保留在同一容器中。另一方面,repartition将对所有分区中的数据进行混洗,因此执行器之间的网络使用率很高,并且会影响性​​能。

coalescerepartition减少分区数相比,性能更好。


有用的解释。
纳伦德拉·马鲁

11

代码和代码文档中得出的结论coalesce(n)是,该代码与相同,coalesce(n, shuffle = false)并且repartition(n)coalesce(n, shuffle = true)

因此,coalescerepartition均可用于增加分区数

使用shuffle = true,您实际上可以合并到更大数量的分区。如果您的分区数量很少(例如100),并且可能有几个分区异常大,那么这将很有用。

要强调的另一个重要注意事项是,如果您大幅减少分区数,则应考虑使用改组版本的coalesce(与repartition那种情况相同)。这将允许您在父分区上并行执行计算(多个任务)。

但是,如果您要进行剧烈的合并,例如对numPartitions = 1,这可能会导致您的计算在比您希望的节点更少的节点上进行(例如在情况下为一个节点numPartitions = 1)。为避免这种情况,您可以通过shuffle = true。这将增加一个随机播放步骤,但是意味着当前的上游分区将并行执行(无论当前分区是什么)。

在这里也参考相关答案


10

所有的答案都在这个非常常见的问题中增加了一些丰富的知识。

因此,按照这个问题时间表的传统,这是我的2美分。

在非常特定的情况下,我发现重新分区的速度比合并更快

在我的应用程序中,当我们估计的文件数量低于特定阈值时,重新分区的工作速度更快。

这就是我的意思

if(numFiles > 20)
    df.coalesce(numFiles).write.mode(SaveMode.Overwrite).parquet(dest)
else
    df.repartition(numFiles).write.mode(SaveMode.Overwrite).parquet(dest)

在上面的代码段中,如果我的文件少于20个,则合并需要花费很多时间,而重新分区要快得多,因此上面的代码也是如此。

当然,这个数字(20)将取决于工作人员的数量和数据量。

希望能有所帮助。


6

重新分区:将数据重新整理为新的分区数量。

例如。初始数据帧分为200个分区。

df.repartition(500):数据将从200个分区改组到新的500个分区。

合并:将数据混入现有的分区数。

df.coalesce(5):数据将从剩余的195个分区改组为5个现有分区。


3

我想补充一下贾斯汀和鲍尔的答案-

repartition将忽略现有分区并创建新分区。因此,您可以使用它来修复数据偏斜。您可以提及分区键来定义分布。数据偏斜是“大数据”问题空间中的最大问题之一。

coalesce将与现有分区一起使用,并对其一部分进行随机组合。它不能像修复数据偏斜那样多repartition。因此,即使价格便宜,也可能不是您所需要的。


3

对于所有出色的答案,我想补充一点,这repartition是利用数据并行化的最佳选择之一。虽然coalesce提供了减少分区的廉价选择,但在将数据写入HDFS或其他某些接收器以利用大写操作时,这非常有用。

我发现以镶木地板格式写入数据以充分利用这一点很有用。


2

对于有人在从PySpark(AWS EMR)生成单个csv文件作为输出并将其保存到s3时遇到问题,可以使用重新分区。原因是,合并不能完全改组,但是重新分区可以。本质上,您可以使用重新分区来增加或减少分区的数量,但只能通过合并来减少分区的数量(但不能减少1)。这是尝试从AWS EMR到s3编写CSV的任何人的代码:

df.repartition(1).write.format('csv')\
.option("path", "s3a://my.bucket.name/location")\
.save(header = 'true')

0

用简单的方法COALESCE:-仅用于减少分区数,它不会压缩数据,只是压缩分区

REPARTITION:-用于增加和减少分区数,但发生改组

例:-

val rdd = sc.textFile("path",7)
rdd.repartition(10)
rdd.repartition(2)

两者都很好

但是,当我们需要在一个集群中查看输出时,我们通常会考虑这两个方面,我们会这样做。


9
Coalese也将进行数据移动。
sun_dare

0

但是,如果要处理大量数据,则还应确保即将合并的节点的数据具有较高的配置。因为所有数据都将被加载到那些节点,所以可能导致内存异常。尽管赔偿费用很高,但我还是喜欢使用它。由于它会随机播放并平均分配数据。

明智的选择是合并还是重新分区。


0

repartition算法对数据进行完全混洗,并创建大小相等的数据分区。coalesce合并现有分区以避免完全洗牌。

Coalesce非常适合采用具有很多分区的RDD并将单个工作节点上的分区组合在一起以产生具有更少分区的最终RDD。

Repartition将重新排列RDD中的数据以产生您请求的最终分区数。DataFrames的分区似乎是应该由框架管理的低级实施细节,但并非如此。在将大型DataFrame过滤为较小的DataFrame时,几乎应该总是对数据重新分区。您可能会经常将大型DataFrame过滤为较小的DataFrame,因此习惯了重新分区。

如果您想了解更多详细信息,请阅读此博客文章

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.