Come sovrascrivere la directory di output in spark


107

Ho un'applicazione Spark Streaming che produce un set di dati per ogni minuto. Ho bisogno di salvare / sovrascrivere i risultati dei dati elaborati.

Quando ho provato a sovrascrivere il set di dati org.apache.hadoop.mapred.FileAlreadyExistsException interrompe l'esecuzione.

Ho impostato la proprietà Spark set("spark.files.overwrite","true"), ma non c'è fortuna.

Come sovrascrivere o predelete i file da Spark?


1
Sì, fa schifo non è vero, lo considero una regressione a 0.9.0. Si prega di accettare la mia risposta :)
samthebest

set("spark.files.overwrite","true")funziona solo per i file aggiunti attraversospark.addFile()
aiman

Risposte:


106

AGGIORNAMENTO: suggerisci di utilizzare Dataframes, oltre a qualcosa di simile ... .write.mode(SaveMode.Overwrite) ....

Pratico magnaccia:

implicit class PimpedStringRDD(rdd: RDD[String]) {
    def write(p: String)(implicit ss: SparkSession): Unit = {
      import ss.implicits._
      rdd.toDF().as[String].write.mode(SaveMode.Overwrite).text(p)
    }
  }

Per le versioni precedenti prova

yourSparkConf.set("spark.hadoop.validateOutputSpecs", "false")
val sc = SparkContext(yourSparkConf)

Nella 1.1.0 puoi impostare le impostazioni di configurazione usando lo script spark-submit con il flag --conf.

ATTENZIONE (versioni precedenti): Secondo @piggybox c'è un bug in Spark in cui sovrascriverà solo i file necessari per scrivere i suoi part-file, qualsiasi altro file verrà lasciato non rimosso.


29
Per Spark 1.4:df.write.mode(SaveMode.Overwrite).parquet(path)
Ha Pham

Per Spark SQL, hai opzioni per definire il SaveMode per Core Spark, non hai niente del genere. Mi piacerebbe davvero un po 'di quel tipo di funzionalità per saveAsTextFile e altre trasformazioni
Murtaza Kanchwala

3
Un problema nascosto: rispetto alla soluzione di @ pzecevic per cancellare l'intera cartella tramite HDFS, in questo approccio Spark sovrascriverà solo i file di parti con lo stesso nome di file nella cartella di output. Questo funziona la maggior parte del tempo, ma se nella cartella sono presenti qualcos'altro come file di parti extra da un altro lavoro Spark / Hadoop, questi file non verranno sovrascritti.
salvadanaio

6
È inoltre possibile utilizzare la df.write.mode(mode: String).parquet(path)modalità Where: La stringa può essere: "sovrascrivi", "aggiungi", "ignora", "errore".
segale

1
@avocado Sì, penso di sì, le API Spark peggiorano sempre di più ad ogni versione: P
samthebest


27

La documentazione per il parametro spark.files.overwritedice questo: "Se sovrascrivere i file aggiunti SparkContext.addFile()quando il file di destinazione esiste e il suo contenuto non corrisponde a quello dell'origine". Quindi non ha alcun effetto sul metodo saveAsTextFiles.

Puoi farlo prima di salvare il file:

val hadoopConf = new org.apache.hadoop.conf.Configuration()
val hdfs = org.apache.hadoop.fs.FileSystem.get(new java.net.URI("hdfs://localhost:9000"), hadoopConf)
try { hdfs.delete(new org.apache.hadoop.fs.Path(filepath), true) } catch { case _ : Throwable => { } }

Come spiegato qui: http://apache-spark-user-list.1001560.n3.nabble.com/How-can-I-make-Spark-1-0-saveAsTextFile-to-overwrite-existing-file-td6696. html


29
che ne dici di pyspark?
javadba

La prossima risposta da usare 'write.mode (SaveMode.Overwrite)' è la strada da percorrere
YaOg

hdfs può eliminare i nuovi file non appena arrivano poiché sta ancora eliminando quelli vecchi.
Jake

25

Dalla documentazione pyspark.sql.DataFrame.save (attualmente a 1.3.1), è possibile specificare mode='overwrite'quando si salva un DataFrame:

myDataFrame.save(path='myPath', source='parquet', mode='overwrite')

Ho verificato che questo rimuoverà anche i file di partizione rimasti. Quindi, se originariamente avessi detto 10 partizioni / file, ma poi sovrascrivi la cartella con un DataFrame che aveva solo 6 partizioni, la cartella risultante avrà le 6 partizioni / file.

Vedere la documentazione di Spark SQL per ulteriori informazioni sulle opzioni della modalità.


2
Vero e utile, grazie, ma una soluzione specifica per DataFrame spark.hadoop.validateOutputSpecsfunzionerà con tutte le API Spark.
samthebest

Per qualche motivo, spark.hadoop.validateOutputSpecsnon ha funzionato per me su 1.3, ma funziona.
Eric Walker

1
@samthebest Con la save(... , mode=route, è possibile sovrascrivere un set di file, aggiungerne un altro, ecc. all'interno dello stesso contesto Spark. Non ti spark.hadoop.validateOutputSpecslimiterebbe a una sola modalità per contesto?
dnlbrky

1
@ dnlbrky L'OP non ha chiesto di aggiungere. Come ho detto, vero, utile, ma non necessario. Se l'OP chiedesse "come aggiungo", allora potrebbe essere fornita un'intera gamma di risposte. Ma non entriamo in questo. Inoltre, ti consiglio di prendere in considerazione l'utilizzo della versione Scala di DataFrames poiché ha l'indipendenza dai tipi e un maggiore controllo, ad esempio se avessi un errore di battitura in "sovrascrittura" non lo scopriresti finché quel DAG non è stato valutato - cosa che in un lavoro Big Data potrebbe essere 2 ore dopo !! Se usi la versione Scala, il compilatore controllerà tutto in anticipo! Abbastanza interessante e molto importante per i Big Data.
samthebest

15

df.write.mode('overwrite').parquet("/output/folder/path")funziona se vuoi sovrascrivere un file parquet usando python. Questo è in Spark 1.6.2. L'API potrebbe essere diversa nelle versioni successive


Sì, funziona alla grande per le mie esigenze (Databricks)
Nick.McDermaid il

4
  val jobName = "WordCount";
  //overwrite the output directory in spark  set("spark.hadoop.validateOutputSpecs", "false")
  val conf = new 
  SparkConf().setAppName(jobName).set("spark.hadoop.validateOutputSpecs", "false");
  val sc = new SparkContext(conf)

Solo per Spark 1, nell'ultima versione usadf.write.mode(SaveMode.Overwrite)
ChikuMiku

3

Questa versione sovraccarica del file save funzione di funziona per me:

yourDF.save (outputPath, org.apache.spark.sql.SaveMode.valueOf ("Overwrite"))

L'esempio sopra sovrascriverebbe una cartella esistente. La modalità di salvataggio può accettare anche questi parametri ( https://spark.apache.org/docs/1.4.0/api/java/org/apache/spark/sql/SaveMode.html ):

Aggiungi : la modalità Aggiungi significa che quando si salva un DataFrame in un'origine dati, se i dati / la tabella esistono già, il contenuto del DataFrame dovrebbe essere aggiunto ai dati esistenti.

ErrorIfExists : la modalità ErrorIfExists significa che quando si salva un DataFrame in un'origine dati, se i dati esistono già, si prevede che venga generata un'eccezione.

Ignora : la modalità Ignora significa che quando si salva un DataFrame in un'origine dati, se i dati esistono già, l'operazione di salvataggio dovrebbe non salvare il contenuto del DataFrame e non modificare i dati esistenti.


1

Se sei disposto a utilizzare il tuo formato di output personalizzato, sarai in grado di ottenere il comportamento desiderato anche con RDD.

Dai un'occhiata alle seguenti classi: FileOutputFormat , FileOutputCommitter

Nel formato di output del file hai un metodo chiamato checkOutputSpecs, che controlla se la directory di output esiste. In FileOutputCommitter hai il commitJob che di solito trasferisce i dati dalla directory temporanea al suo posto finale.

Non sono stato ancora in grado di verificarlo (lo farei, non appena ho pochi minuti liberi) ma in teoria: se estendo FileOutputFormat e sovrascrivo checkOutputSpecs su un metodo che non genera eccezioni sulla directory già esistente, e aggiusto il commitJob del mio committer di output personalizzato per eseguire la logica che voglio (ad esempio sovrascrivere alcuni file, aggiungerne altri) potrei essere in grado di ottenere il comportamento desiderato anche con gli RDD.

Il formato di output viene passato a: saveAsNewAPIHadoopFile (che è il metodo chiamato anche saveAsTextFile per salvare effettivamente i file). E il committer di uscita è configurato a livello di applicazione.


Eviterei di avvicinarmi alla sottoclasse di FileOutputCommitter se puoi evitarlo: è un pezzo di codice spaventoso. Hadoop 3.0 aggiunge un punto di plug-in in cui FileOutputFormat può accettare diverse implementazioni di una superclasse con refactoring (PathOutputCommitter). Quello S3 di Netflix verrà scritto sul posto in un albero partizionato, eseguendo solo la risoluzione dei conflitti (fallire, eliminare, aggiungere) al job commit e solo nelle partizioni aggiornate
stevel,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.