Spark-repartition()とcoalesce()


254

Learning Sparkによると

データの再パーティション化はかなりコストのかかる操作であることを覚えておいてください。Sparkには、データの移動を回避できるように最適化されたバージョンのrepartition()呼び出されたバージョンもありcoalesce()ますが、RDDパーティションの数を減らしている場合のみです。

私が得る1つの違いはrepartition()、パーティションの数を増やす/減らすことができるがcoalesce()、パーティションの数を減らすことしかできないということです。

パーティションが複数のマシンに分散してcoalesce()実行されている場合、どのようにしてデータの移動を回避できますか?

回答:


354

完全なシャッフルを回避します。数が減少していることがわかっている場合、エグゼキューターはデータを最小数のパーティションに安全に保持でき、追加のノードから保持したノードにのみデータを移動できます。

したがって、次のようになります。

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では、元のデータを移動する必要がないことに注意してください。


115
ご回答ありがとうございます。ドキュメンテーションはのminimize data movement代わりに言ったほうがいいでしょうavoiding data movement
Praveen Sripati 2015

12
repartition代わりに使用すべきケースはありますcoalesceか?
Niemand

21
@Niemand私は現在のドキュメントがこれをかなりうまくカバーしていると思います:github.com/apache/spark/blob/…すべてrepartitionが行うことはcoalesceshuffleパラメーターをtrueに設定して呼び出すことです。それが役立つかどうか私に知らせてください。
Justin Pihony

2
既存のパーティションファイルの数を減らすことは可能ですか?hdfsはありませんが、多くのファイルに問題があります。

2
再分割は、それが縮小していることを知らないため、統計的に遅くなります...多分彼らはそれを最適化することができますが。内部的には、shuffle = trueフラグを使用して合体を呼び出すだけです
Justin Pihony 2016年

171

ジャスティンの答えは素晴らしいです、そして、この反応はより深く入ります。

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方法では、新しいパーティションが作成され、新しいパーティションにデータが均等に分散されます(データの分散は、データセットが大きいほど均一になります)。

coalescerepartition

coalesce既存のパーティションを使用して、シャッフルされるデータの量を最小限に抑えます。 repartition新しいパーティションを作成し、完全にシャッフルします。 coalesce結果として、データ量が異なるパーティション(サイズが大きく異なるパーティション)が作成さrepartitionれ、ほぼ同じサイズのパーティションが作成されます。

であるcoalesceか、repartitionより高速な?

coalesceはよりも高速に実行できますがrepartition、サイズが等しくないパーティションは、通常、サイズが等しいパーティションよりも動作が遅くなります。通常、大きなデータセットをフィルタリングした後、データセットを再分割する必要があります。repartitionSparkは同じサイズのパーティションで動作するように構築されているため、全体的に高速であることがわかりました。

注意:不思議なことに、パーティション分割するとディスク上のデータのサイズが増加する可能性があることに気付きました。大きなデータセットでrepartition /合体を使用している場合は、必ずテストを実行してください。

詳細については、このブログ投稿をご覧ください。

実際に合体と再分割を使用する場合


8
@Powersという素晴らしい答えですが、パーティションAとBのデータは歪んでいませんか?どのように均等に分散されますか?
anwartheravian 2016

また、OOMエラーが発生せずにパーティションサイズを取得するための最良の方法は何ですか。私は使用しrdd.glom().map(len).collect()ていますが、OOMエラーがたくさん発生します。
anwartheravian 2016

8
@anwartheravian- repartitionアルゴリズムは非常に小さなデータセットの場合と同じようにデータを分散しないため、パーティションAとパーティションBのサイズは異なります。以前repartitionは500万のレコードを13のパーティションに整理していましたが、各ファイルは89.3 MBから89.6 MBの間でした。
Powers

1
@Powersこれは詳細でよりよく答えるように見えます。
Green

1
これは違いをよりよく説明します。ありがとう!
Abhi

22

ここでもう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

良いですね!これは重要であり、少なくともこの経験豊富なscala devにとっては明白ではありません。つまり、再パーティション化合体も、データを変更する試みではなく、ノード間でのデータの分散方法です
doug

1
@Harikrishnanなので、他の答えを正しく理解していれば、Coalesceの場合のようにSparkは既存のパーティションを使用しますが、RDDは不変なので、Coalesceが既存のパーティションをどのように使用するか説明できますか?私の理解によると、Sparkは新しいパーティションを既存のパーティションに合体して追加すると思いました。
エクスプローラー

しかし、「古い」RDDが使用されなくなった場合、実行グラフからわかるように、永続化しないとメモリからクリアされますよね。
Markus

15

repartition -パーティションの数を増やしながら使用することをお勧めします。これは、すべてのデータのシャッフルが含まれるためです。

coalesce-パーティションの数を減らしながら使用することをお勧めします。たとえば、3つのパーティションがあり、それを2に減らしたい場合はcoalesce、3番目のパーティションデータをパーティション1と2に移動します。パーティション1と2は同じコンテナに残ります。一方、repartitionはすべてのパーティションのデータをシャッフルするため、エグゼキューター間のネットワーク使用率が高くなり、パフォーマンスに影響します。

coalescerepartitionパーティション数を減らすよりもパフォーマンスが向上します。


役立つ説明。
Narendra Maru

11

コードとコードドキュメントから続くのcoalesce(n)は、と同じcoalesce(n, shuffle = false)repartition(n)あり、同じです。coalesce(n, shuffle = true)

したがって、との両方coalescerepartition使用して、パーティション数を増やすことができます。

を使用するとshuffle = true、実際にはより多くのパーティションに合体できます。これは、パーティションの数が少ない場合(100など)、いくつかのパーティションが異常に大きくなる可能性がある場合に役立ちます。

強調するもう1つの重要な注意事項は、パーティションの数を大幅に減らす場合は、シャッフルバージョンcoalescerepartitionその場合と同じ)の使用を検討する必要があることです。これにより、親パーティションで並列に計算を実行できます(複数のタスク)。

ただし、たとえばに劇的な合体を行っている場合numPartitions = 1は、計算が必要なノードよりも少ないノード(たとえばの場合は1つのノード)で計算が行われる可能性がありますnumPartitions = 1。これを回避するには、を渡しshuffle = trueます。これによりシャッフル手順が追加されますが、現在の上流のパーティションが並列に実行されます(現在のパーティションが何であっても)。

こちらの関連する回答も参照してください


10

すべての回答は、この非常によく寄せられる質問にいくつかの素晴らしい知識を加えています。

この質問のタイムラインの伝統に従って、ここに私の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)は、ワーカーの数とデータの量によって異なります。

お役に立てば幸いです。


6

Repartition:データを新しい数のパーティションにシャッフルします。

例えば。初期データフレームは200パーティションに分割されます。

df.repartition(500):データは200パーティションから新しい500パーティションにシャッフルされます。

Coalesce:データを既存の数のパーティションにシャッフルします。

df.coalesce(5):データは残りの195パーティションから5つの既存のパーティションにシャッフルされます。


3

ジャスティンとパワーの答えに追加したいと思います-

repartition既存のパーティションを無視して、新しいパーティションを作成します。したがって、これを使用してデータの歪みを修正できます。分配を定義するためにパーティションキーに言及することができます。データスキューは、「ビッグデータ」問題領域における最大の問題の1つです。

coalesce既存のパーティションで動作し、それらのサブセットをシャッフルします。データのずれを修正することはできませんrepartition。したがって、それがより安価であっても、それはあなたが必要とするものではないかもしれません。


3

私が付け加えたいすばらしい答えすべてに、それrepartitionはデータの並列化を利用する最良のオプションの1つです。一方でcoalesce、パーティションを減らすための安価なオプションが提供され、大量の書き込みを利用するためにHDFSまたは他のシンクにデータを書き込むときに非常に役立ちます。

これは、データを寄木細工の形式で書き込んで、最大限に活用する場合に便利です。


2

PySpark(AWS EMR)から単一のcsvファイルを出力として生成し、それをs3に保存するのに問題があった人のために、再パーティション化の使用が役立ちました。その理由は、合体は完全なシャッフルを行うことはできませんが、再分割は可能です。基本的に、repartitionを使用してパーティションの数を増減できますが、coalesceを使用してパーティションの数を減らすことができます(1はできません)。AWS EMRからs3にcsvを書き込もうとしている人のためのコードは次のとおりです。

df.repartition(1).write.format('csv')\
.option("path", "s3a://my.bucket.name/location")\
.save(header = 'true')

0

簡単な方法でCOALESCE:-はパーティションの数を減らすためだけです、パーティションを圧縮するだけでデータのシャッフルはありません

REPARTITION:-は、パーティション数の増加と減少の両方のためですが、シャッフルが行われます

例:-

val rdd = sc.textFile("path",7)
rdd.repartition(10)
rdd.repartition(2)

どちらも正常に動作します

ただし、1つのクラスターで出力を確認する必要がある場合は、通常、この2つのことを行います。


9
Coaleseでのデータの移動もあります。
sun_dare

0

ただし、巨大なデータを扱う場合は、合体ノードに送られるデータを高度に構成する必要があります。すべてのデータがこれらのノードにロードされるため、メモリ例外が発生する可能性があります。賠償には費用がかかりますが、私はそれを使用することを好みます。データをシャッフルして均等に分散するため。

合体と再分割のどちらかを選択するのは賢明です。


0

repartitionこのアルゴリズムは、データの完全なシャッフルを行い、データの同じサイズのパーティションを作成します。coalesce既存のパーティションを組み合わせて、完全なシャッフルを回避します。

Coalesceは、多数のパーティションを持つRDDを取得し、単一のワーカーノードでパーティションを組み合わせて、パーティションの少ない最終的なRDDを生成するのに適しています。

RepartitionRDD内のデータを再シャッフルして、要求したパーティションの最終的な数を生成します。DataFrameの分割は、フレームワークで管理する必要がある低レベルの実装の詳細のように見えますが、そうではありません。大きなデータフレームを小さなデータフレームにフィルタリングする場合、ほとんどの場合、データを再分割する必要があります。おそらく、大規模なDataFrameを小さいものに頻繁にフィルタリングするので、再パーティション化に慣れます。

詳細については、このブログ投稿をご覧ください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.