web-dev-qa-db-fra.com

partitionBy & overwrite stratégie dans un Azure DataLake à l'aide de PySpark dans Databricks

J'ai un processus ETL simple dans un environnement Azure

stockage d'objets blob> datafactory> datalake raw> databricks> datalake curated> datwarehouse (ETL principal).

les jeux de données pour ce projet ne sont pas très volumineux (~ 1 million de lignes, 20 colonnes donnent ou prennent) mais j'aimerais les garder correctement partitionnés dans mon datalake en tant que fichiers Parquet.

actuellement, je lance une logique simple pour déterminer où dans mon lac chaque fichier doit se trouver en fonction des calendriers professionnels.

les fichiers ressemblent vaguement à ceci

Year Week Data
2019 01   XXX
2019 02   XXX

Je partitionne ensuite un fichier donné dans le format suivant en remplaçant les données existantes et en créant de nouveaux dossiers pour les nouvelles données.

curated ---
           dataset --
                     Year 2019 
                              - Week 01 - file.pq + metadata
                              - Week 02 - file.pq + metadata
                              - Week 03 - file.pq + datadata #(pre existing file)

les métadonnées sont succès et commits qui sont générées automatiquement.

à cette fin, j'utilise la requête suivante dans Pyspark 2.4.3

dataset.repartition(1).write.mode('overwrite')\
                         .partitionBy('Year','Week').parquet('\curataed\dataset')

maintenant, si j'utilise cette commande seule, elle écrasera toutes les données existantes dans la partition cible

donc Week 03 sera perdu.

l'utilisation de spark.conf.set("spark.sql.sources.partitionOverwriteMode","dynamic") semble arrêter le problème et uniquement surécrire les fichiers cibles, mais je me demande si c'est la meilleure façon de gérer les fichiers dans mon lac de données?

j'ai également eu du mal à trouver de la documentation sur la fonctionnalité ci-dessus.

mon premier instinct a été de boucler sur un seul parquet et d'écrire chaque partition manuellement, ce qui, bien que me donne un meilleur contrôle dans la dénomination des fichiers et le partitionnement ciblé, est à toutes fins utiles, très très lent (et envoie des sonnettes d'alarme provenant d'un pandas état d'esprit)

ma prochaine pensée serait d'écrire chaque partition dans un dossier /tmp et de déplacer chaque fichier parquet, puis de remplacer les fichiers/créer des fichiers selon les besoins en utilisant la requête ci-dessus. puis purgez le dossier /tmp tout en créant une sorte de journal de métadonnées.

Y a-t-il une meilleure façon/méthode pour cela?

toute orientation serait très appréciée.

le but final ici est d'avoir une zone propre et sûre pour toutes les données "curated" tout en ayant un journal des fichiers de parquet que je peux lire dans un DataWarehouse pour plus d'ETL.

5
Manakin

Corrigez-moi si j'ai manqué quelque chose de crucial dans votre approche, mais il semble que vous vouliez écrire de nouvelles données en plus des données existantes, ce qui est normalement fait avec

write.mode('append')

au lieu de 'overwrite'

Si vous souhaitez conserver les données séparées par lot, afin de pouvoir les sélectionner pour le téléchargement dans l'entrepôt de données ou l'audit, il n'y a pas de moyen raisonnable de le faire en plus d'inclure ces informations dans l'ensemble de données et de les partitionner pendant l'enregistrement, par exemple.

dataset.write.mode('append')\
                     .partitionBy('Year','Week', 'BatchTimeStamp').parquet('curated\dataset')

Toute autre intervention manuelle dans le format de fichier parquet sera au mieux piratée, au pire risque de rendre votre pipeline peu fiable ou de corrompre vos données.

Le lac Delta, mentionné par Mohammad, est également une bonne suggestion dans l'ensemble pour stocker de manière fiable des données dans des lacs de données et une norme dorée de l'industrie en ce moment. Pour votre cas d'utilisation spécifique, vous pouvez utiliser sa fonction de faire des requêtes historiques (tout ajouter, puis interroger la différence entre l'ensemble de données actuel et après le lot précédent), mais le journal d'audit est limité dans le temps à la façon dont vous configurez votre lac delta et peut être aussi peu que 7 jours, donc si vous voulez des informations complètes à long terme, vous devez quand même suivre l'approche consistant à enregistrer les informations sur les lots.

À un niveau plus stratégique, lorsque vous suivez raw -> curated -> DW, vous pouvez également envisager d'ajouter un autre `` saut '' et de placer vos données prêtes dans un dossier `` prétraité '', organisé par lot, puis de les ajouter aux ensembles curated et DW .

En remarque, .repartition(1) n'a pas beaucoup de sens lors de l'utilisation de parquets, car le parquet est de toute façon un format multi-fichiers, donc les seuls effets que cela a sont des impacts négatifs sur les performances. Mais faites-le moi savoir s'il y a une raison spécifique pour laquelle vous l'utilisez.

1
Daniel

Au lieu d'écrire directement la table, nous pouvons utiliser saveAsTable avec append et supprimer les partitions avant cela.

dataset.repartition(1).write.mode('append')\
                     .partitionBy('Year','Week').saveAsTable("tablename")

Pour supprimer les partitions précédentes

partitions = [ (x["Year"], x["Week"]) for x in dataset.select("Year", "Week").distinct().collect()]
for year, week in partitions:
    spark.sql('ALTER TABLE tablename DROP IF EXISTS PARTITION (Year = "'+year+'",Week = "'+week+'")')
1
Anshuman