1時間ごとのログファイルを読み取り、データをパーティション分割して保存する必要があるETLプロセスを作成しています。 Spark(Databricksの場合)を使用しています。ログファイルはCSVなので、読み取り、スキーマを適用してから、変換を実行します。
私の問題は、毎時のデータを寄木細工の形式で保存して、既存のデータセットに追加するにはどうすればよいですか?保存するときは、データフレームに存在する4つの列で分割する必要があります。
これが私の保存行です:
_data
.filter(validPartnerIds($"partnerID"))
.write
.partitionBy("partnerID","year","month","day")
.parquet(saveDestination)
_
問題は、宛先フォルダーが存在する場合、保存がエラーをスローすることです。宛先が存在しない場合は、ファイルを追加していません。
.mode("append")
を使用してみましたが、Sparkが途中で失敗することがあるので、データの書き込み量と書き込み量が失われます。
パーティショニングにより将来のクエリが大幅に増えるため、私は寄木細工を使用しています。また、データを何らかのファイル形式でディスクに書き込む必要があり、DruidやCassandraなどのデータベースを使用できません。
データフレームを分割してファイルを保存する方法(寄木細工または他の形式を使用)についての提案は、大歓迎です。
ファイルを追加する必要がある場合は、必ず追加モードを使用する必要があります。生成すると予想されるパーティションの数はわかりませんが、manyパーティションがある場合、partitionBy
によって多くの問題(メモリとIOの問題)。
問題の原因が書き込み操作に時間がかかりすぎていると思われる場合は、次の2つを試すことをお勧めします。
1)設定に追加してsnappyを使用します。
conf.set("spark.sql.parquet.compression.codec", "snappy")
2)次のように、hadoopConfiguration
のSparkContext
にあるメタデータファイルの生成を無効にします。
sc.hadoopConfiguration.set("parquet.enable.summary-metadata", "false")
メタデータファイルは生成にいくらか時間がかかります( this blog post を参照)。しかし this によると、それらは実際には重要ではありません。個人的に、私は常にそれらを無効にし、問題はありません。
多数のパーティション(> 500)を生成する場合、私ができる最善の方法は、appendを使用してnotソリューションを検討することをお勧めすることです-mode-partitionBy
をその数のパーティションで動作させることができませんでした。
ソートされていないパーティションを使用している場合、データはすべてのパーティションに分割されます。つまり、すべてのタスクがデータを生成し、各出力ファイルに書き込みます。
出力ファイルごとのすべてのデータを同じパーティションに置くように書き込む前に、パーティション列に従ってデータを再パーティション化することを検討してください。
data
.filter(validPartnerIds($"partnerID"))
.repartition([optional integer,] "partnerID","year","month","day")
.write
.partitionBy("partnerID","year","month","day")
.parquet(saveDestination)