根据Learning Spark
请记住,对数据重新分区是一项相当昂贵的操作。Spark还具有
repartition()
称为的优化版本,coalesce()
可以避免数据移动,但前提是要减少RDD分区的数量。
我得到的一个区别是repartition()
分区数可以增加/减少,但是coalesce()
分区数只能减少。
如果分区分布在多台计算机上并coalesce()
运行,那么如何避免数据移动?
根据Learning Spark
请记住,对数据重新分区是一项相当昂贵的操作。Spark还具有
repartition()
称为的优化版本,coalesce()
可以避免数据移动,但前提是要减少RDD分区的数量。
我得到的一个区别是repartition()
分区数可以增加/减少,但是coalesce()
分区数只能减少。
如果分区分布在多台计算机上并coalesce()
运行,那么如何避免数据移动?
Answers:
避免完全洗牌。如果知道数量正在减少,那么执行程序可以安全地将数据保留在最小数量的分区上,只需将数据从多余的节点移到我们保留的节点上即可。
因此,它将如下所示:
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不需要移动其原始数据。
repartition
应该代替使用coalesce
吗?
repartition
操作都是coalesce
在将shuffle
参数设置为true的情况下进行的。让我知道是否有帮助。
shuffle = true
标志来称呼合并
贾斯汀的答案太棒了,而且这种回应更加深入。
该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可以与相同大小的分区一起使用。
注意我好奇地发现分区会增加磁盘上的数据大小。在大型数据集上使用重新分区/合并时,请确保运行测试。
如果您想了解更多详细信息,请阅读此博客文章。
在实践中何时使用合并和重新分区
rdd.glom().map(len).collect()
但是它给出了很多OOM错误。
repartition
算法对于很小的数据集不能平均分配数据。我曾经repartition
将500万条记录组织到13个分区中,每个文件在89.3 MB和89.6 MB之间-相当相等!
这里要注意的另一点是,作为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
从代码和代码文档中得出的结论coalesce(n)
是,该代码与相同,coalesce(n, shuffle = false)
并且repartition(n)
与coalesce(n, shuffle = true)
因此,coalesce
和repartition
均可用于增加分区数
使用
shuffle = true
,您实际上可以合并到更大数量的分区。如果您的分区数量很少(例如100),并且可能有几个分区异常大,那么这将很有用。
要强调的另一个重要注意事项是,如果您大幅减少分区数,则应考虑使用改组版本的coalesce
(与repartition
那种情况相同)。这将允许您在父分区上并行执行计算(多个任务)。
但是,如果您要进行剧烈的合并,例如对
numPartitions = 1
,这可能会导致您的计算在比您希望的节点更少的节点上进行(例如在情况下为一个节点numPartitions = 1
)。为避免这种情况,您可以通过shuffle = true
。这将增加一个随机播放步骤,但是意味着当前的上游分区将并行执行(无论当前分区是什么)。
请在这里也参考相关答案
所有的答案都在这个非常常见的问题中增加了一些丰富的知识。
因此,按照这个问题时间表的传统,这是我的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)将取决于工作人员的数量和数据量。
希望能有所帮助。
对于所有出色的答案,我想补充一点,这repartition
是利用数据并行化的最佳选择之一。虽然coalesce
提供了减少分区的廉价选择,但在将数据写入HDFS或其他某些接收器以利用大写操作时,这非常有用。
我发现以镶木地板格式写入数据以充分利用这一点很有用。
用简单的方法COALESCE:-仅用于减少分区数,它不会压缩数据,只是压缩分区
REPARTITION:-用于增加和减少分区数,但发生改组
例:-
val rdd = sc.textFile("path",7)
rdd.repartition(10)
rdd.repartition(2)
两者都很好
但是,当我们需要在一个集群中查看输出时,我们通常会考虑这两个方面,我们会这样做。
但是,如果要处理大量数据,则还应确保即将合并的节点的数据具有较高的配置。因为所有数据都将被加载到那些节点,所以可能导致内存异常。尽管赔偿费用很高,但我还是喜欢使用它。由于它会随机播放并平均分配数据。
明智的选择是合并还是重新分区。
该repartition
算法对数据进行完全混洗,并创建大小相等的数据分区。coalesce
合并现有分区以避免完全洗牌。
Coalesce非常适合采用具有很多分区的RDD并将单个工作节点上的分区组合在一起以产生具有更少分区的最终RDD。
Repartition
将重新排列RDD中的数据以产生您请求的最终分区数。DataFrames的分区似乎是应该由框架管理的低级实施细节,但并非如此。在将大型DataFrame过滤为较小的DataFrame时,几乎应该总是对数据重新分区。您可能会经常将大型DataFrame过滤为较小的DataFrame,因此习惯了重新分区。
如果您想了解更多详细信息,请阅读此博客文章。
minimize data movement
而不是avoiding data movement
。