学習スパークによると
データの再パーティション化はかなり費用のかかる操作です。 Sparkには、coalesce()と呼ばれる最適化されたrepartition()バージョンもあります。これは、データの移動を回避することを可能にします。
1つの違いは、repartition()を使用するとパーティション数を増減できることですが、coalesce()を使用するとパーティション数を減らすことしかできないということです。
パーティションが複数のマシンにまたがってcoalesce()が実行されている場合、どのようにしてデータの移動を防ぐことができますか?
それはフルシャッフルを避けます。数が減っていることがわかっていれば、エクゼキュータは安全にデータを最小数のパーティションに保持し、余分なノードからデータを移動したノードに移動するだけです。
だから、それはこのようなものになるでしょう:
Node 1 = 1,2,3
Node 2 = 4,5,6
Node 3 = 7,8,9
Node 4 = 10,11,12
それからcoalesce
2パーティションまで:
Node 1 = 1,2,3 + (10,11,12)
Node 3 = 7,8,9 + (4,5,6)
ノード1とノード3は元のデータを移動する必要がないことに注意してください。
ジャスティンの答えは素晴らしいです、そしてこの反応はもっと深く入ります。
repartition
アルゴリズムはフルシャッフルを行い、均等に分散されたデータで新しいパーティションを作成します。 1から12までの数字でDataFrameを作成しましょう。
val x = (1 to 12).toList
val numbersDf = x.toDF("number")
私のマシンにはnumbersDf
に4つのパーティションがあります。
numbersDf.rdd.partitions.size // => 4
データがパーティション上でどのように分割されるかは次のとおりです。
Partition 00000: 1, 2, 3
Partition 00001: 4, 5, 6
Partition 00002: 7, 8, 9
Partition 00003: 10, 11, 12
repartition
メソッドを使ってフルシャッフルし、このデータを2つのノードで取得しましょう。
val numbersDfR = numbersDf.repartition(2)
これが私のマシン上でnumbersDfR
データがどのように分割されるかです:
Partition A: 1, 3, 4, 6, 7, 9, 10, 12
Partition B: 2, 5, 8, 11
repartition
メソッドは、新しいパーティションを作成し、そのデータを新しいパーティションに均等に分配します(データ分配は、より大きなデータセットに対してより均等です)。
coalesce
とrepartition
の違い
coalesce
は、シャッフルされるデータ量を最小限に抑えるために既存のパーティションを使用します。 repartition
は新しいパーティションを作成し、フルシャッフルを行います。 coalesce
は異なる量のデータを持つパーティション(時にはサイズがかなり違うパーティション)になり、repartition
はほぼ同じサイズのパーティションになります。
coalesce
またはrepartition
は速いですか?
coalesce
はrepartition
より速く動作するかもしれませんが、異なるサイズのパーティションは同じサイズのパーティションよりも作業が遅くなります。大規模なデータセットをフィルタリングした後は、通常、データセットを再分割する必要があります。 Sparkは同じサイズのパーティションで動作するように構築されているため、repartition
は全体的に高速であることがわかりました。
このブログ記事 を読んでください。
ここで注意すべきもう1つのポイントは、Spark RDDの基本原則は不変性であるということです。再分割または合体によって、新しいRDDが作成されます。基本RDDは、元の数のパーティションと存在し続けます。ユースケースがキャッシュにRDDを保持することを要求している場合、新しく作成されたRDDについても同じことが行われる必要があります。
scala> pairMrkt.repartition(10)
res16: org.Apache.spark.rdd.RDD[(String, Array[String])] =MapPartitionsRDD[11] at repartition at <console>:26
scala> res16.partitions.length
res17: Int = 10
scala> pairMrkt.partitions.length
res20: Int = 2
すべての答えは、この非常によくある質問にいくつかの素晴らしい知識を追加しています。
この質問のスケジュールの伝統に従って、ここに私の2セントがあります。
私は、分割が合体よりも速いことを発見しました。
私のアプリケーションでは、私たちが推定するファイルの数が特定のしきい値より少ないとき、再分割はより速く動作します。
これが私の言っていることです
if(numFiles > 20)
df.coalesce(numFiles).write.mode(SaveMode.Overwrite).parquet(dest)
else
df.repartition(numFiles).write.mode(SaveMode.Overwrite).parquet(dest)
上記のスニペットでは、私のファイルが20個未満の場合、再分割がはるかに高速になる一方で、上記のコードの場合、合体は完了するまでに永遠にかかりました。
もちろん、この数(20)は作業者の数とデータの量によって異なります。
それが役立つことを願っています。
repartition
- 全データのシャッフルを伴うため、パーティション数を増やしながらパーティション分割を使用することをお勧めします。
coalesce
-パーティション数を減らしながら、合体を使用することをお勧めします。たとえば、3つのパーティションがあり、それを2つのパーティションに減らしたい場合、Coalesceは3番目のパーティションのデータをパーティション1と2に移動します。パーティション1と2は同じコンテナ内に残ります。エグゼキュータ間のパフォーマンスが高くなり、パフォーマンスに影響します。
パフォーマンス面でcoalesce
のパフォーマンスはrepartition
よりも優れていますが、パーティション数は削減されています。
私が付け加えたいのは、再パーティション化はデータ並列化を利用するための最良の選択肢の1つであり、Coalesceはパーティションを縮小するための安価なオプションを提供します。大きな書き込み。私はこれを有効に活用するために寄木細工フォーマットでデータを書くときに便利だと思いました。
簡単な方法でCOALESCE: - パーティションの数を減らすためだけのもので、データのシャッフリングなしでパーティションを圧縮するだけです
REPARTITION: - パーティションの数を増減するためのものですが、シャッフルは行われます
例: -
val rdd = sc.textFile("path",7)
rdd.repartition(10)
rdd.repartition(2)
両方ともうまく機能する
しかし、1つのクラスター内で出力を確認する必要がある場合は、一般的にこの2つのことを行います。これを使用します。
code およびコードドキュメントからわかることは、coalesce(n)
はcoalesce(n, shuffle = false)
と同じであり、repartition(n)
はcoalesce(n, shuffle = true)
と同じであるということです。
したがって、coalesce
とrepartition
の両方を使用して、パーティションの数を増やすことができます
Shuffle = trueの場合、実際にはより多くのパーティションに合体できます。これは、少数のパーティション(たとえば100)があり、いくつかのパーティションが異常に大きい可能性がある場合に役立ちます。
強調すべきもう1つの重要な注意点は、劇的に減少パーティションの数を指定する場合、shuffledバージョンのcoalesce
(その場合はrepartition
と同じ)の使用を検討する必要があるということです。これにより、計算を実行できるようになります親パーティションで並列に(複数のタスク)。
ただし、抜本的な合体を行っている場合は、 numPartitions = 1の場合、これにより計算が必要なノードよりも少なくなる可能性があります(たとえば、numPartitions = 1の場合は1つのノード)。これを回避するには、shuffle = trueを渡すことができます。これはシャッフルステップを追加しますが、現在のアップストリームパーティションが(現在のパーティションが何であれ)並列に実行されることを意味します。
関連する回答も参照してください here
ただし、巨大なデータを扱う場合は、ノードが合体するデータが高度に構成されている必要があります。すべてのデータがそれらのノードにロードされるため、メモリ例外が発生する可能性があります。賠償は費用がかかりますが、私はそれを使うのが好きです。それはデータを均等にシャッフルして分配するからです。
合体と再分割を選択するのが賢明です。
PySparkから単一のcsvファイル(AWS EMR)を出力として生成し、それをs3に保存することに問題がある人のために、repartitionを使用すると役に立ちました。その理由は、合体は完全なシャッフルを行うことができないが、再分割はできるということです。基本的に、再分割を使用してパーティションの数を増減できますが、合体を使用してパーティションの数を減らすことができるだけです(1は除く)。これはAWS EMRからs3へcsvを書き込もうとしている人のためのコードです。
df.repartition(1).write.format('csv')\
.option("path", "s3a://my.bucket.name/location")\
.save(header = 'true')
私はJustinとPowerの答えにそれを付け加えたいと思います -
"repartition"は既存のパーティションを無視して新しいパーティションを作成します。そのため、データの偏りを修正するためにそれを使用できます。分配を定義するためにパーティションキーを挙げることができます。データスキューは、「ビッグデータ」問題領域の最大の問題の1つです。
「合体」は既存のパーティションを処理し、それらのサブセットをシャッフルします。 「再分割」のようにデータの偏りを修正することはできません。それほど安価であってもそれはあなたが必要とするものではないかもしれません。