web-dev-qa-db-ja.com

Spark= DataFrameのパーティションキーを知っていますか?

Spark=寄木細工のファイルの分割キーを知っていて、シャッフルを避けるためにこの情報を使用するかどうかを知りたいです。

コンテキスト:

実行中Spark 2.0.1ローカルSparkSessionを実行しています。ディスク上に寄木細工ファイルとして保存しているcsvデータセットがあります。

val df0 = spark
  .read
  .format("csv")
  .option("header", true)
  .option("delimiter", ";")
  .option("inferSchema", false)
  .load("SomeFile.csv"))


val df = df0.repartition(partitionExprs = col("numerocarte"), numPartitions = 42)

df.write
  .mode(SaveMode.Overwrite)
  .format("parquet")
  .option("inferSchema", false)
  .save("SomeFile.parquet")

numerocarteで42個のパーティションを作成しています。これにより、複数のnumerocarteを同じパーティションにグループ化する必要があります。 write時にpartitionBy( "numerocarte")を実行したくないのは、カードごとに1つのパーティションが必要ないためです。それは何百万人になります。

その後、別のスクリプトでこれを読みましたSomeFile.parquetファイルを寄せ集め、その上でいくつかの操作を行います。特に、window functionその上で、寄木細工のファイルが再分割されたのと同じ列で分割が行われます。

import org.Apache.spark.sql.expressions.Window
import org.Apache.spark.sql.functions._

val df2 = spark.read
  .format("parquet")
  .option("header", true)
  .option("inferSchema", false)
  .load("SomeFile.parquet")

val w = Window.partitionBy(col("numerocarte"))
.orderBy(col("SomeColumn"))

df2.withColumn("NewColumnName",
      sum(col("dollars").over(w))

readの後、repartitionが期待どおりに機能し、DataFrame df2には42個のパーティションがあり、それぞれに異なるカードがあります。

質問:

  1. Sparkはデータフレームdf2は列numerocarte?でパーティション分割されていますか?
  2. わかっている場合、ウィンドウ関数にシャッフルはありません。本当?
  3. わからない場合は、ウィンドウ関数でシャッフルを行います。本当?
  4. わからない場合は、Sparkが正しい列でデータが既にパーティション分割されていることをどのように伝えますか?
  5. DataFrameのパーティションキーを確認するにはどうすればよいですか?これのためのコマンドはありますか?パーティションの数を確認する方法は知っていますが、パーティションキーを確認する方法はありますか?
  6. 各ステップの後にファイル内のパーティションの数を印刷すると、readの後に42パーティション、withColumnの後に200パーティションになり、SparkがDataFrame
  7. 同じ列で再パーティション化された2つの異なるテーブルがある場合、結合はその情報を使用しますか?
13
astro_asz

Sparkは、データフレームdf2が列numerocarteでパーティション分割されていることを知っていますか?

ありません。

わからない場合は、Sparkが正しい列でデータが既にパーティション分割されていることをどのように伝えますか?

あなたはしません。シャッフルされたデータを保存したからといって、同じ分割でロードされることを意味するわけではありません。

DataFrameのパーティションキーを確認するにはどうすればよいですか?

データを読み込んだ後はパーティションキーはありませんが、queryExecutionPartitionerを確認できます。


実際には:

  • キーの効率的なプッシュダウンをサポートする場合は、partitionByDataFrameWriterメソッドを使用します。
  • 結合の最適化の限定的なサポートが必要な場合は、メタストアと永続テーブルでbucketByを使用します。

詳細な例については、 DataFrameのパーティション分割の定義方法 を参照してください。

12
hi-zir

今後の参考のために、自分の質問に答えています。

@ user8371915の提案に従って、bucketByは機能します!

DataFrame dfを保存しています:

df.write
  .bucketBy(250, "userid")
  .saveAsTable("myNewTable")

次に、このテーブルをロードする必要がある場合:

val df2 = spark.sql("SELECT * FROM myNewTable")

val w = Window.partitionBy("userid")

val df3 = df2.withColumn("newColumnName", sum(col("someColumn")).over(w)
df3.explain

私は確認しますuseridでパーティション分割されたdf2でウィンドウ関数を実行するとシャッフルはありません!ありがとう@ user8371915!

調査中に学んだこと

  • myNewTableは通常の寄せ木細工のファイルのように見えますが、そうではありません。通常はspark.read.format("parquet").load("path/to/myNewTable")で読むことができますが、この方法で作成されたDataFrameは元のパーティションを保持しません!正しくパーティション化されたselectを取得するには、spark.sqlDataFrameを使用する必要があります。
  • spark.sql("describe formatted myNewTable").collect.foreach(println)を使用してテーブル内を見ることができます。これにより、バケットに使用された列とバケットの数がわかります。
  • パーティション化を利用するウィンドウ関数と結合では、多くの場合、ソートも必要です。 .sortBy()を使用して、書き込み時にバケット内のデータを並べ替えることができ、並べ替えはHiveテーブルにも保存されます。 df.write.bucketBy(250, "userid").sortBy("somColumnName").saveAsTable("myNewTable")
  • ローカルモードで作業している場合、テーブルmyNewTableはローカルScala SBTプロジェクトのspark-warehouseフォルダーに保存されます。spark-submitを介してmesosを使用してクラスターモードで保存する場合、私にとっては、/user/Hive/warehouseにありました。
  • spark-submitを実行する場合、SparkSessionに2つのオプションを追加する必要があります:.config("Hive.metastore.uris", "thrift://addres-to-your-master:9083").enableHiveSupport()。そうしないと、作成したHiveテーブルは表示されません。
  • テーブルを特定のデータベースに保存する場合は、バケット化する前にspark.sql("USE your database")を実行します。

2018年5月2日更新

sparkバケットとHiveテーブルの作成で問題が発生しました。 理由はSpark saveAsTable with bucketBy何千ものファイルを作成しますか?

8
astro_asz