在Spark"中总是随机播放"移动数据,即使是在微不足道的情况下?

时间:2015-08-21 09:02:36

标签: performance apache-spark

在包含分区的Spark数据集的简单情况下,其中每个密钥仅出现在单个分区中,就像以下两个分区一样:

  1. [(" a",1),(" a",2)]
  2. [(" b",1)],
  3. 一个shuffle操作(如groupByKey)通常会对分区中的数据进行洗牌,即使不需要这样做吗?

    我问这个问题,因为改组是昂贵的,所以这对于大型数据集来说很重要。我的用例正是这样的:一个大型数据集,其中每个键几乎总是位于一个分区中。

1 个答案:

答案 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