web-dev-qa-db-fra.com

Ajouter de nouvelles données à des fichiers de parquet partitionnés

J'écris un processus ETL dans lequel je devrai lire les fichiers journaux horaires, partitionner les données et les sauvegarder. J'utilise Spark (dans Databricks) . Les fichiers journaux sont au format CSV. Je les lis donc, leur applique un schéma, puis j'effectue mes transformations. 

Mon problème est, comment puis-je sauvegarder les données de chaque heure au format parquet, mais les ajouter au jeu de données existant? Lors de la sauvegarde, je dois partitionner par 4 colonnes présentes dans le cadre de données. 

Voici ma ligne de sauvegarde:

data
    .filter(validPartnerIds($"partnerID"))
    .write
    .partitionBy("partnerID","year","month","day")
    .parquet(saveDestination)

Le problème est que si le dossier de destination existe, la sauvegarde génère une erreur. Si la destination n’existe pas, je n’ajoute pas mes fichiers.

J'ai essayé d'utiliser .mode("append") mais je constate que Spark échoue parfois à mi-parcours. Je finis donc par perdre la quantité de mes données écrites et celle qu'il me reste à écrire. 

J'utilise du parquet car le partitionnement augmente considérablement mes interrogations à l'avenir. De plus, je dois écrire les données sous forme de format de fichier sur disque et je ne peux pas utiliser une base de données telle que Druid ou Cassandra.

Toutes les suggestions sur la manière de partitionner mon dataframe et de sauvegarder les fichiers (que ce soit collé au parquet ou à un autre format) sont grandement appréciées. 

13
Saman

Si vous devez ajouter les fichiers, vous devez absolument utiliser le mode Ajout. Je ne sais pas combien de partitions vous espérez générer, mais je constate que si vous avez plusieurs partitions, partitionBy causera un certain nombre de problèmes (problèmes de mémoire et d'E/S identiques).

Si vous pensez que votre problème est causé par des opérations d'écriture prenant trop de temps, je vous recommande d'essayer ces deux choses:

1) Utilisez snappy en ajoutant à la configuration: 

conf.set("spark.sql.parquet.compression.codec", "snappy")

2) Désactivez la génération des fichiers de métadonnées dans hadoopConfiguration sur SparkContext comme ceci:

sc.hadoopConfiguration.set("parquet.enable.summary-metadata", "false")

Les fichiers de métadonnées prendront un peu de temps à générer (voir cet article de blog ), mais selon ceci ils ne sont pas réellement importants. Personnellement, je les désactive toujours et n'ai aucun problème. 

Si vous générez de nombreuses partitions (> 500), je crains que le mieux que je puisse faire est de vous suggérer de rechercher une solution pas en utilisant le mode append. Je n'ai simplement jamais réussi à faire fonctionner partitionBy avec autant de partitions.

11

Si vous utilisez un partitionnement non trié, vos données seront réparties sur toutes vos partitions. Cela signifie que chaque tâche va générer et écrire des données dans chacun de vos fichiers de sortie.

Pensez à repartitionner vos données en fonction des colonnes de votre partition avant d'écrire pour avoir toutes les données par fichier de sortie sur les mêmes partitions:

data
 .filter(validPartnerIds($"partnerID"))
 .repartition([optional integer,] "partnerID","year","month","day")
 .write
 .partitionBy("partnerID","year","month","day")
 .parquet(saveDestination)

Voir: DataFrame.repartition

0
MrChrisRodriguez