Spark - repartition()vs coalesce()

时间:2015-07-24 12:49:20

标签: apache-spark distributed-computing rdd

根据Learning Spark

  

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

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

如果分区分布在多台机器上并且运行了coalesce(),它怎么能避免数据移动?

18 个答案:

答案 0 :(得分:254)

避免完整随机播放。如果已知该数字正在减少,则执行程序可以安全地将数据保存在最小数量的分区上,仅将数据从额外节点移出到我们保留的节点上。

所以,它会是这样的:

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不需要移动原始数据。

答案 1 :(得分:113)

贾斯汀的答案非常棒,而且这种反应更深入。

match-destinations算法执行完全shuffle并创建具有均匀分布的数据的新分区。让我们创建一个数字框架,其数字从1到12。

repartition

val x = (1 to 12).toList val numbersDf = x.toDF("number") 在我的计算机上包含4个分区。

numbersDf

以下是如何在分区上划分数据:

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 方法创建新分区并在新分区中均匀分布数据(对于较大的数据集,数据分布更均匀)。

repartitioncoalesce

之间的差异

repartition使用现有分区来最小化混洗的数据量。 coalesce创建新分区并进行完全随机播放。 repartition导致具有不同数据量的分区(有时是大小不同的分区),coalesce会产生大小相等的分区。

repartitioncoalesce更快?

repartition可能比coalesce运行得更快,但不等大小的分区通常比相同大小的分区更慢。在过滤大型数据集后,您通常需要重新分区数据集。我发现repartition整体上更快,因为Spark是为了使用相同大小的分区而构建的。

Read this blog post如果你想了解更多细节。

答案 2 :(得分:16)

此处需要注意的另一点是,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

答案 3 :(得分:7)

所有答案都在这个经常被问到的问题中添加了一些很好的知识。

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

在非常具体的情况下,我发现重新分区比合并更快。

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

这就是我的意思

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

在上面的代码片段中,如果我的文件少于20,那么coalesce将永远完成,而重新分区要快得多,所以上面的代码也是如此。

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

希望有所帮助。

答案 4 :(得分:3)

if (location.protocol === 'https:') { // is https } else { // is http } -建议您在不增加任何分区的情况下使用重新分区,因为它涉及对所有数据的改组。

repartition-建议在减少分区数量的同时使用合并。例如,如果您有3个分区,并且想要将其减少到2个分区,则Coalesce会将第3个分区数据移至分区1和2。分区1和2将保留在同一Container中。但是重新分区将在所有分区中重新排列数据,因此网络使用率执行器之间的间隔会很高,并且会影响性​​能。

明智的性能coalescecoalesce更好,同时减少了分区数量。

答案 5 :(得分:3)

基本上重新分区允许您增加或减少分区的数量。重新分区会重新分配所有分区中的数据,这会导致 full shuffle,这是非常昂贵的操作。

Coalesce 是 Repartition 的优化版本,您只能减少分区数量。由于我们只能减少分区的数量,它所做的就是将一些分区合并为一个分区。通过合并分区,与重新分区相比,跨分区的数据移动更少。所以在 Coalesce 中是最小的数据移动,但说 coalesce 不进行数据移动是完全错误的说法。

另一件事是通过提供分区数量进行重新分区,它尝试在所有分区上均匀地重新分配数据,而在 Coalesce 的情况下,我们在某些情况下仍然可能有偏斜数据。

答案 6 :(得分:2)

以一种简单的方式 COALESCE: - 仅用于减少分区的数量,不压缩数据只是压缩分区

REPARTITION: - 用于增加和减少分区的数量,但是洗牌发生了

实施例: -

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

两者都很好

但是当我们需要在一个集群中看到输出时,我们通常会考虑这两件事,我们就这样做了。

答案 7 :(得分:2)

○ 合并使用现有分区来最小化混洗的数据量。重新分区会创建新分区并进行完全洗牌。

○ 合并导致具有不同数据量的分区(有时分区具有许多不同的大小),而重新分区导致大小大致相同的分区。

○ 合并我们可以减少分区,但我们可以增加和减少分区。

答案 8 :(得分:2)

对于所有很棒的答案,我想补充一点,重新分区是利用数据并行化的最佳选择之一,而合并提供了便宜的选择来减少分区,并且在将数据写入HDFS或其他一些接收器时非常有用。利用大写。 我发现以镶木地板格式写入数据以充分利用这一点很有用。

答案 9 :(得分:1)

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

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

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

Coalesce:将数据随机排列到现有的分区中

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

答案 10 :(得分:1)

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

Coalesce可以很好地用于具有多个分区的RDD,并在单个工作节点上组合多个分区以生成具有较少分区的最终RDD。

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

Read this blog post,如果您需要更多详细信息。

答案 11 :(得分:0)

合并比重新分区表现更好。合并总是减少分区。假设你在 yarn 中启用动态分配,你有四个分区和执行器。如果对其应用过滤器,则一个或多个执行器可能是空的,没有数据。这个问题可以通过合并而不是重新分区来解决。

答案 12 :(得分:0)

有一个重新分区的用例>>即使@Rob的答案中提到的分区号减少,即将数据写入单个文件,也会合并。

@Rob的答案暗示了正确的方向,但我认为需要进一步解释才能了解幕后情况。

如果您需要在写入之前过滤数据,那么 repartition coalesce 更合适,因为合并将在加载操作之前被下推。 / p>

例如: from itertools import combinations k = 6 assert k // 2 a = 0 b = 1 l = [ [a if i in combination else b for i in range(k)] for combination in combinations(range(k), int(k/2)) ]

翻译成: load().map(…).filter(…).coalesce(1).save()

这意味着所有数据都将折叠到一个分区中,在该分区中将对其进行过滤,从而失去所有并行性。 即使对于非常简单的过滤器(例如“ column ='value”),也会发生这种情况。

这不会发生在分区上:load().coalesce(1).map(…).filter(…).save()

在这种情况下,过滤会在原始分区上并行进行。

只是给出一个数量级,以我为例,当从Hive表加载后用约1000个分区过滤109M行(〜105G)时,运行时间从合并(1)的〜6h降至重新分区的〜2m (1)。

具体示例取自this article from AirBnB,这很好,涵盖了Spark中重新分区技术的更多方面。

答案 13 :(得分:0)

另一个差异是考虑了倾斜连接并且您必须在其上合并的情况。在大多数情况下,重新分区可以解决偏斜连接,然后就可以合并。

另一种情况是,假设您已在数据框中保存了中/大量数据,并且必须批量生产给Kafka。在某些情况下,重新分区有助于在生成Kafka之前收集collectionasList。但是,当卷确实很高时,重新分区可能会严重影响性能。在这种情况下,直接从数据框生成Kafka会有所帮助。

旁注:与工作人员之间的完整数据移动一样,Coalcece不会避免数据移动。它确实减少了洗牌的次数。我认为这就是本书的意思。

答案 14 :(得分:0)

code和代码文档得出的结论是,coalesce(n)coalesce(n, shuffle = false)相同,而repartition(n)coalesce(n, shuffle = true)相同

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

  

使用shuffle = true时,您实际上可以合并为更大的数字     分区。如果您的分区数量很少,这很有用,     例如100,可能有几个分区异常大。

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

  

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

另请参阅相关答案here

答案 15 :(得分:0)

对于在使用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')

答案 16 :(得分:0)

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

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

“ coalesce”将与现有分区一起使用,并对其一部分进行随机组合。它不能像“分区”一样多地解决数据偏斜。因此,即使价格便宜,也不一定是您需要的东西。

答案 17 :(得分:0)

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

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