当我使用<input ... required ...>
或reduceByKey
时,我遇到了分区问题。
离)aggregateByKey
特别是,如果输入数据偏斜,使用上述方法时分区问题会变得更糟。
因此,作为解决方案,我使用reduceBykey(_+_).map(code)
方法。
例如,http://dev.sortable.com/spark-repartition/类似。
这适用于分区分发,但repartition
也很昂贵。
有没有办法明智地解决分区问题?
答案 0 :(得分:3)
你是对的,
重新分区的运行成本非常高。由于洗牌和其他小步骤。如你所示,创建一个例子就像这样说:
rdd.map(x => (x, x * x)).repartition(8).reduceByKey(_+_)
请参阅此处的DAG:
此步骤将在DAG创建一个地图,一个重新分区和一个减少。
但如果您在reduceByKey
内使用重新分区,则可以对“免费”进行重新分区。
Repratition的主要部分是Shuffle,而reduceByKey的主要部分也是shuffle。您可以在Scala lib中看到reduceByKey
有一个numPartition parameter。
所以你可以改变你的代码:
rdd.map(x => (x, x * x)).reduceByKey(_+_, 8)
你可以在reduceByKey
中看到与重新分区相同的代码,速度要快得多。因为你可以少做一次。
答案 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)
可以解决问题。