web-dev-qa-db-ja.com

新しいデータのないパーティションを削除せずにSpark=でDataFrameをパーティション分割して書き込む方法は?

DataFrameを使用してDataFrameWriterをParquet形式でHDFSに保存しようとしていますが、次のように3つの列値で分割されています。

dataFrame.write.mode(SaveMode.Overwrite).partitionBy("eventdate", "hour", "processtime").parquet(path)

この質問 で述べたように、partitionBypathにあるパーティションの既存の完全な階層を削除し、dataFrameのパーティションに置き換えます。特定の日の新しい増分データが定期的に入ってくるので、私が望むのは、dataFrameがデータを持っている階層内のパーティションのみを置き換え、他のパーティションはそのままにしておきます。

これを行うには、次のようなフルパスを使用して各パーティションを個別に保存する必要があります:

singlePartition.write.mode(SaveMode.Overwrite).parquet(path + "/eventdate=2017-01-01/hour=0/processtime=1234567890")

しかし、フルパスを使用してデータを書き出すことができるように、データを単一パーティションDataFramesに整理する最良の方法を理解するのに苦労しています。一つのアイデアは次のようなものでした:

dataFrame.repartition("eventdate", "hour", "processtime").foreachPartition ...

ただし、foreachPartitionIterator[Row]これは、Parquet形式への書き込みには理想的ではありません。

また、select...distinct eventdate, hour, processtimeパーティションのリストを取得し、それらのパーティションごとに元のデータフレームをフィルタリングし、その結果を完全なパーティションパスに保存します。ただし、個別のクエリと各パーティションのフィルターは、多くのフィルター/書き込み操作になるため、あまり効率的ではありません。

dataFrameにデータがない既存のパーティションを保存するよりクリーンな方法があることを望んでいますか?

読んでくれてありがとう。

Sparkバージョン:2.1

25
jaywilson

モードオプションAppendにはキャッチがあります!

df.write.partitionBy("y","m","d")
.mode(SaveMode.Append)
.parquet("/data/Hive/warehouse/mydbname.db/" + tableName)

私はテストして、これが既存のパーティションファイルを保持することを確認しました。ただし、今回の問題は次のとおりです。同じコードを2回(同じデータで)実行すると、同じデータの既存のパーケットファイルを置き換えるのではなく、新しいパーケットファイルを作成します(Spark 1.6)。したがって、Appendを使用する代わりに、Overwriteを使用してこの問題を解決できます。テーブルレベルで上書きする代わりに、パーティションレベルで上書きする必要があります。

df.write.mode(SaveMode.Overwrite)
.parquet("/data/Hive/warehouse/mydbname.db/" + tableName + "/y=" + year + "/m=" + month + "/d=" + day)

詳細については、次のリンクを参照してください。

spark dataframe write method で特定のパーティションを上書きする

(スリヤントのコメントの後、返信を更新しました。Thnx。)

12
newwebdev

これは非常に古いことを知っています。投稿されたソリューションが表示されないため、先に投稿します。このアプローチは、書き込み先のディレクトリにHiveテーブルがあることを前提としています。この問題に対処する1つの方法は、dataFrameから一時ビューを作成し、それをテーブルに追加してから、通常のHiveのようなinsert overwrite table ...コマンド:

dataFrame.createOrReplaceTempView("temp_view")
spark.sql("insert overwrite table table_name partition ('eventdate', 'hour', 'processtime')select * from temp_view")

古いパーティションを保持しながら、新しいパーティションにのみ(上書き)書き込みます。

4
D3V

これは古いトピックですが、私は同じ問題を抱えていて、別の解決策を見つけました。次のコマンドを使用して、パーティションの上書きモードを動的に設定するだけです。

spark.conf.set('spark.sql.sources.partitionOverwriteMode', 'dynamic')

したがって、私のsparkセッションは次のように構成されます。

spark = SparkSession.builder.appName('AppName').getOrCreate()
spark.conf.set('spark.sql.sources.partitionOverwriteMode', 'dynamic')
0
rodrigombs