web-dev-qa-db-ja.com

Spark dataframe / datasetの効率的な結合のためのパーティションデータ

いくつかの共有キー列に基づいて、join多くのDataFramesを一緒に必要とします。キー値RDDの場合、パーティショナーを指定して、同じキーを持つデータポイントが同じエグゼキューターにシャッフルされるように、結合がより効率的になります(joinの前にシャッフル関連の操作がある場合)。同じことをSpark DataFramesまたはDataSetsで実行できますか?

6
Rainfield

複数回参加することがわかっている場合は、ロード後にrepartition DataFrameを使用できます

val users = spark.read.load("/path/to/users").repartition('userId)

val joined1 = users.join(addresses, "userId")
joined1.show() // <-- 1st shuffle for repartition

val joined2 = users.join(salary, "userId")
joined2.show() // <-- skips shuffle for users since it's already been repartitioned

したがって、データを1回シャッフルし、その後の時間に参加するときにシャッフルファイルを再利用します。

ただし、特定のキーのデータを繰り返しシャッフルすることがわかっている場合は、データをバケット化されたテーブルとして保存することをお勧めします。これにより、事前にパーティション分割されたデータが書き出されるため、テーブルを読み込んで結合するときにシャッフルを回避できます。次のようにできます。

// you need to pick a number of buckets that makes sense for your data
users.bucketBy(50, "userId").saveAsTable("users")
addresses.bucketBy(50, "userId").saveAsTable("addresses")

val users = spark.read.table("users")
val addresses = spark.read.table("addresses")

val joined = users.join(addresses, "userId")
joined.show() // <-- no shuffle since tables are co-partitioned

シャッフルを回避するために、テーブルは同じバケットを使用する必要があります(たとえば、同じ数のバケットとバケット列での結合)。

12
Silvio

repartitionメソッドを使用してDataFrame/DataSet APIを使用することができます。この方法を使用すると、データパーティションに使用する1つまたは複数の列を指定できます。

val df2 = df.repartition($"colA", $"colB")

同じコマンドで必要なパーティションの数を同時に指定することもできます。

val df2 = df.repartition(10, $"colA", $"colB")

注:これは、データフレームのパーティションが同じノードに配置されることを保証するものではなく、パーティション化が同じ方法で行われることのみを保証します。