假设我们有一个PySpark数据帧,数据在2048个分区中均匀分布,我们希望合并到32个分区,将数据写回HDFS。使用coalesce
很好,因为它不需要昂贵的随机播放。
但是coalesce
的一个缺点是,它通常会导致新分区中数据的分布不均匀。我假设这是因为原始分区ID被散列到新的分区ID空间,并且冲突的数量是随机的。
但是,原则上应该可以均匀地合并,以便将原始数据帧的前64个分区发送到新数据帧的第一个分区,接下来的64个分区发送到第二个分区,结束,导致分区均匀分布。结果数据帧通常更适合进一步计算。
这是否可行,同时防止洗牌?
我可以使用this question中的技巧强制初始分区和最终分区之间的关系,但Spark不知道每个原始分区的所有内容都将转到特定的新分区。因此,它无法优化洗牌,并且运行速度比coalesce
慢得多。
答案 0 :(得分:1)
在您的情况下,您可以安全地将2048个分区合并为32,并假设Spark将上游分区均匀分配给合并的分区(在您的情况下为64个)。
以下是an extract from the Scaladoc of RDD#coalesce
:
这导致狭窄的依赖性,例如如果你从1000个分区转到100个分区,那么就不会有一个shuffle,而是100个新分区中的每个分区将声明10个当前分区。
还要考虑您的分区在群集中的物理分布方式会影响合并的发生方式。以下是CoalescedRDD's ScalaDoc的摘录:
如果父级中没有位置信息(没有preferredLocations),则合并非常简单:在块中以阵列形式接近的块父级。 如果有地点信息,它会继续打包以下四个目标:
(1)平衡组,使它们大致具有相同数量的父分区
(2)实现每个分区的位置,即找到大多数父分区更喜欢的一台机器
(3)高效,即n个父分区的O(n)算法(问题很可能是NP难的)
(4)平衡首选机器,即尽可能避免选择相同的首选机器