J'essaie de sauvegarder un DataFrame
sur HDFS au format Parquet en utilisant DataFrameWriter
, partitionné en trois valeurs de colonne, comme ceci:
dataFrame.write.mode(SaveMode.Overwrite).partitionBy("eventdate", "hour", "processtime").parquet(path)
Comme mentionné dans cette question , partitionBy
supprimera la hiérarchie complète existante des partitions sur path
et les remplacera par les partitions de dataFrame
. Etant donné que de nouvelles données incrémentielles pour un jour particulier arriveront périodiquement, ce que je veux, c'est remplacer uniquement les partitions de la hiérarchie pour lesquelles dataFrame
possède des données, en laissant les autres intactes.
Pour ce faire, il me semble que je dois enregistrer chaque partition individuellement en utilisant son chemin complet, comme ceci:
singlePartition.write.mode(SaveMode.Overwrite).parquet(path + "/eventdate=2017-01-01/hour=0/processtime=1234567890")
Cependant, j'ai du mal à comprendre le meilleur moyen d'organiser les données en une seule partition DataFrame
s afin que je puisse les écrire en utilisant leur chemin complet. Une idée était quelque chose comme:
dataFrame.repartition("eventdate", "hour", "processtime").foreachPartition ...
Mais foreachPartition
fonctionne sur un Iterator[Row]
qui n’est pas idéal pour écrire au format Parquet.
J'ai aussi envisagé d'utiliser un select...distinct eventdate, hour, processtime
pour obtenir la liste des partitions, puis filtrer le cadre de données d'origine par chacune de ces partitions et enregistrer les résultats dans leur chemin de partitionnement complet. Mais la requête distincte associée à un filtre pour chaque partition ne semble pas très efficace, car ce serait beaucoup d'opérations de filtrage/écriture.
J'espère qu'il existe un moyen plus propre de conserver les partitions existantes pour lesquelles dataFrame
n'a pas de données?
Merci d'avoir lu.
Version Spark: 2.1
L'option de mode Append
a un piège!
df.write.partitionBy("y","m","d")
.mode(SaveMode.Append)
.parquet("/data/Hive/warehouse/mydbname.db/" + tableName)
J'ai testé et vu que cela conservera les fichiers de partition existants. Cependant, le problème est cette fois-ci le suivant: Si vous exécutez le même code deux fois (avec les mêmes données), il créera de nouveaux fichiers de parquet au lieu de remplacer ceux qui existent déjà pour les mêmes données (Spark 1.6). Donc, au lieu d'utiliser Append
, nous pouvons toujours résoudre ce problème avec Overwrite
. Au lieu d'écraser au niveau de la table, nous devrions écraser au niveau de la partition.
df.write.mode(SaveMode.Overwrite)
.parquet("/data/Hive/warehouse/mydbname.db/" + tableName + "/y=" + year + "/m=" + month + "/d=" + day)
Voir le lien suivant pour plus d'informations:
Écraser des partitions spécifiques dans spark méthode d'écriture de données)
(J'ai mis à jour ma réponse après le commentaire de suriyanto. Thnx.)
Je sais que c'est très vieux. Comme je ne vois aucune solution affichée, je vais en poster une. Cette approche suppose que vous avez une table Hive sur le répertoire dans lequel vous souhaitez écrire. Une façon de résoudre ce problème consiste à créer une vue temporaire à partir de dataFrame
, qui devrait être ajoutée à la table, puis à utiliser normale Hive-like insert overwrite table ...
commande:
dataFrame.createOrReplaceTempView("temp_view")
spark.sql("insert overwrite table table_name partition ('eventdate', 'hour', 'processtime')select * from temp_view")
Il préserve les anciennes partitions tout en écrasant (sur) uniquement les nouvelles partitions.
C'est un vieux sujet, mais j'avais le même problème et j'ai trouvé une autre solution. Réglez simplement le mode d'écrasement de votre partition sur dynamique en utilisant:
spark.conf.set('spark.sql.sources.partitionOverwriteMode', 'dynamic')
Ainsi, ma session spark est configurée comme suit:
spark = SparkSession.builder.appName('AppName').getOrCreate()
spark.conf.set('spark.sql.sources.partitionOverwriteMode', 'dynamic')