在包含分区的Spark数据集的简单情况下,其中每个密钥仅出现在单个分区中,就像以下两个分区一样:
一个shuffle操作(如groupByKey)通常会对分区中的数据进行洗牌,即使不需要这样做吗?
我问这个问题,因为改组是昂贵的,所以这对于大型数据集来说很重要。我的用例正是这样的:一个大型数据集,其中每个键几乎总是位于一个分区中。
答案 0 :(得分:2)
嗯,这取决于。默认情况下,groupByKey
使用HashPartitioner
。让我们假设您只有两个分区。这意味着带有键“a”的对将转到分区号1
scala> "a".hashCode % 2
res17: Int = 1
并将键“b”与分区2配对
scala> "b".hashCode % 2
res18: Int = 0
如果你这样创建RDD:
val rdd = sc.parallelize(("a", 1) :: ("a", 2) :: ("b", 1) :: Nil, 2).cache
有多种情况如何分配数据。首先,我们需要一个小帮手:
def addPartId[T](iter: Iterator[T]) = {
Iterator((TaskContext.get.partitionId, iter.toList))
}
情景1
rdd.mapPartitions(addPartId).collect
Array((0,List((b,1))), (1,List((a,1), (a,2))))
由于所有对都已在右侧分区
,因此无需数据移动场景2
Array((0,List((a,1), (a,2))), (1,List((b,1))))
虽然匹配对已经在同一个分区上,但由于分区ID与密钥
不匹配,因此必须移动所有对场景3
某些混合发行版,其中只需移动部分数据:
Array((0,List((a,1))), (1,List((a,2), (b,1))))
如果在HashPartioner
之前使用groupByKey
对数据进行分区,则无需进行随机播放。
val rddPart = rdd.partitionBy(new HashPartitioner(2)).cache
rddPart.mapPartitions(addPartId).collect
Array((0,List((b,1))), (1,List((a,1), (a,2))))
rddPart.groupByKey