在Spark中使用reduceByKey时是否有有效的分区方法?

时间:2017-03-26 09:55:11

标签: apache-spark rdd partitioning

当我使用<input ... required ...>reduceByKey时,我遇到了分区问题。

离)aggregateByKey

特别是,如果输入数据偏斜,使用上述方法时分区问题会变得更糟。

因此,作为解决方案,我使用reduceBykey(_+_).map(code)方法。

例如,http://dev.sortable.com/spark-repartition/类似。

这适用于分区分发,但repartition也很昂贵。

有没有办法明智地解决分区问题?

2 个答案:

答案 0 :(得分:3)

你是对的,

重新分区的运行成本非常高。由于洗牌和其他小步骤。如你所示,创建一个例子就像这样说:

rdd.map(x => (x, x * x)).repartition(8).reduceByKey(_+_)

请参阅此处的DAG:

enter image description here

此步骤将在DAG创建一个地图,一个重新分区和一个减少。

但如果您在reduceByKey内使用重新分区,则可以对“免费”进行重新分区。

Repratition的主要部分是Shuffle,而reduceByKey的主要部分也是shuffle。您可以在Scala lib中看到reduceByKey有一个numPartition parameter

所以你可以改变你的代码:

rdd.map(x => (x, x * x)).reduceByKey(_+_, 8)

enter image description here

你可以在reduceByKey中看到与重新分区相同的代码,速度要快得多。因为你可以少做一次。

enter image description here

答案 1 :(得分:2)

你必须区分两个不同的问题:

数据倾斜

如果数据分布严重偏差(让我们假设只有一个唯一密钥的最坏情况),那么根据定义,输出将会出现偏差,更改分区程序无法帮助您。

有一些技术可用于部分解决问题,但总体分区不是核心问题。

分区偏见

即使数据均匀分布,选择不当的分区功能也可能导致数据分布偏差。例如:

val rdd = sc.parallelize(Seq((5, None), (10, None), (15, None), (20, None)), 5)
rdd
  .partitionBy(new org.apache.spark.HashPartitioner(5))
  .glom.map(_.size).collect
Array[Int] = Array(4, 0, 0, 0, 0)

正如您所看到的那样,尽管密钥分布没有偏差,但hashCode的数据规律性和不良特性导致了偏差。

如果这样选择不同的Partitioner

rdd
  .partitionBy(new org.apache.spark.RangePartitioner(5, rdd))
  .glom.map(_.size).collect
Array[Int] = Array(1, 1, 1, 1, 0)

或调整现有属性:

rdd
  .partitionBy(new org.apache.spark.HashPartitioner(7))
  .glom.map(_.size).collect
Array[Int] = Array(0, 1, 0, 1, 0, 1, 1)

可以解决问题。