web-dev-qa-db-ja.com

パーティション間でデータのバランスをとる方法は?

編集:答えは役に立ちますが、私の解決策を次のように説明しました: SparkのmemoryOverheadの問題


他の人が作成したデータセットを読み取る202092パーティションのRDDがあります。データがパーティション間でバランスが取れていないことを手動で確認できます。たとえば、一部の画像は0で、その他は4kですが、平均は432です。データを処理すると、次のエラーが発生します。

Container killed by YARN for exceeding memory limits. 16.9 GB of 16 GB physical memory used. Consider boosting spark.yarn.executor.memoryOverhead.

memoryOverheadはすでにブーストされています。スパイクが指定された境界をオーバーフローするため、Yarnがコンテナを強制終了するスパイクが発生しているように感じます。

では、データが(大まかに)パーティション間でバランスが取れていることを確認するにはどうすればよいですか?


私の考えは repartition() が機能し、シャッフルを呼び出すというものでした。

dataset = dataset.repartition(202092)

しかし、 programming-guide の指示にもかかわらず、まったく同じエラーが発生しました。

repartition(numPartitions)

RDD内のデータをランダムに再シャッフルして、パーティションの数を増やしたり減らしたりして、パーティション間でバランスを取ります。これにより、常にネットワーク上のすべてのデータがシャッフルされます。


私のおもちゃの例をチェックしてください:

data = sc.parallelize([0,1,2], 3).mapPartitions(lambda x: range((x.next() + 1) * 1000))
d = data.glom().collect()
len(d[0])     # 1000
len(d[1])     # 2000
len(d[2])     # 3000
repartitioned_data = data.repartition(3)
re_d = repartitioned_data.glom().collect()
len(re_d[0])  # 1854
len(re_d[1])  # 1754
len(re_d[2])  # 2392
repartitioned_data = data.repartition(6)
re_d = repartitioned_data.glom().collect()
len(re_d[0])  # 422
len(re_d[1])  # 845
len(re_d[2])  # 1643
len(re_d[3])  # 1332
len(re_d[4])  # 1547
len(re_d[5])  # 211
repartitioned_data = data.repartition(12)
re_d = repartitioned_data.glom().collect()
len(re_d[0])  # 132
len(re_d[1])  # 265
len(re_d[2])  # 530
len(re_d[3])  # 1060
len(re_d[4])  # 1025
len(re_d[5])  # 145
len(re_d[6])  # 290
len(re_d[7])  # 580
len(re_d[8])  # 1113
len(re_d[9])  # 272
len(re_d[10]) # 522
len(re_d[11]) # 66
11
gsamaras

問題を超えるメモリオーバーヘッド制限は、フェッチ中に使用されるDirectMemoryバッファが原因だと思います。 2.0.0で修正されたと思います。 (同じ問題が発生しましたが、2.0.0にアップグレードすると問題が解決したことがわかったため、深く掘り下げるのをやめました。残念ながら、バックアップするSpark発行番号がありません。)


repartitionの後の不均一なパーティションは驚くべきものです。 https://github.com/Apache/spark/blob/v2.0.0/core/src/main/scala/org/Apache/spark/rdd/RDD.scala#L44 と対比してください。 Spark repartitionにランダムなキーを生成することもあるため、バイアスがかかる可能性のあるハッシュでは実行されません。

私はあなたの例を試してみて、exactと同じ結果をSpark 1.6.2およびSpark 2.0.0。ただし、Scala spark-Shell

scala> val data = sc.parallelize(1 to 3, 3).mapPartitions { it => (1 to it.next * 1000).iterator }
data: org.Apache.spark.rdd.RDD[Int] = MapPartitionsRDD[6] at mapPartitions at <console>:24

scala> data.mapPartitions { it => Iterator(it.toSeq.size) }.collect.toSeq
res1: Seq[Int] = WrappedArray(1000, 2000, 3000)

scala> data.repartition(3).mapPartitions { it => Iterator(it.toSeq.size) }.collect.toSeq
res2: Seq[Int] = WrappedArray(1999, 2001, 2000)

scala> data.repartition(6).mapPartitions { it => Iterator(it.toSeq.size) }.collect.toSeq
res3: Seq[Int] = WrappedArray(999, 1000, 1000, 1000, 1001, 1000)

scala> data.repartition(12).mapPartitions { it => Iterator(it.toSeq.size) }.collect.toSeq
res4: Seq[Int] = WrappedArray(500, 501, 501, 501, 501, 500, 499, 499, 499, 499, 500, 500)

こんなに美しい仕切り!


(申し訳ありませんが、これは完全な答えではありません。これまでの調査結果を共有したかっただけです。)

5
Daniel Darabos