Як перезаписати вихідний каталог в іскру


107

У мене є програма для іскрового потокового передавання, яка виробляє набір даних на кожну хвилину. Мені потрібно зберегти / перезаписати результати оброблюваних даних.

Коли я намагався замінити набір даних org.apache.hadoop.mapred.FileAlreadyExistsException, зупиняє виконання.

Я встановив властивість Spark set("spark.files.overwrite","true"), але немає удачі.

Як перезаписати або попередньо виділити файли з іскри?


1
Так, це гадно, чи не так, я вважаю це регресом до 0,9,0. Прийміть мою відповідь :)
samthebest

set("spark.files.overwrite","true")працює тільки для файлів доданий Повсюдноspark.addFile()
Айман

Відповіді:


106

ОНОВЛЕННЯ: Запропонувати використання Dataframesплюс щось подібне ... .write.mode(SaveMode.Overwrite) ....

Зручний сутенер:

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)
    }
  }

Для старих версій спробуйте

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

У 1.1.0 ви можете встановити конфігураційні налаштування, використовуючи скрипт подання іскри із прапором --conf.

УВАГА (старіші версії): Відповідно до @piggybox, в Spark є помилка, де вона буде перезаписувати лише ті файли, які їй потрібні, щоб писати part-файли, а інші файли залишатимуться без змін.


29
Для Spark 1.4:df.write.mode(SaveMode.Overwrite).parquet(path)
Ха-Фам

Для Spark SQL у вас є варіанти визначити SaveMode для Core Spark, у вас немає нічого подібного. Дуже хотілося б отримати якусь подібну функцію для saveAsTextFile та інших перетворень
Муртаза Канчула

3
Прихована проблема: порівнюючи з рішенням @ pzecevic для видалення всієї папки через HDFS, у цьому підході Spark буде лише перезаписати файли частин з тим самим іменем файлу у вихідній папці. Це працює більшу частину часу, але якщо в папці є щось інше, наприклад, додаткові файли із іншого завдання Spark / Hadoop, це не перезапише ці файли.
скарбничка

6
Ви також можете використовувати df.write.mode(mode: String).parquet(path)режим де: String може бути: "перезаписати", "додати", "ігнорувати", "помилка".
жито

1
@avocado Yup так вважаю, API Spark просто погіршується і погіршується з кожним випуском: P
samthebest


27

Документація для цього параметра spark.files.overwriteговорить так: "Чи потрібно перезаписувати файли, додані через, SparkContext.addFile()коли існує цільовий файл і його вміст не відповідає вмісту джерела." Отже, це не впливає на метод saveAsTextFiles.

Ви можете зробити це перед збереженням файлу:

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 => { } }

Aas пояснив тут: http://apache-spark-user-list.1001560.n3.nabble.com/How-can-I-make-Spark-1-0-saveAsTextFile-to-overwrite-existing-file-td6696. html


29
як щодо pyspark?
javadba

Наступна відповідь на використання "write.mode (SaveMode.Overwrite)" - це спосіб піти
YaOg

hdfs може видаляти нові файли під час їх надходження, оскільки він досі видаляє старі.
Джейк

25

З документації pyspark.sql.DataFrame.save (зараз на 1.3.1) ви можете вказати mode='overwrite'під час збереження DataFrame:

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

Я переконався, що це навіть видалить ліві файли розділів. Отже, якщо ви сказали спочатку 10 розділів / файлів, але потім перезаписали папку з DataFrame, який мав лише 6 розділів, в отриманій папці буде 6 розділів / файлів.

Дивіться документацію Spark SQL для отримання додаткової інформації про параметри режиму.


2
Справжнє і корисне, дякую, але специфічне рішення для DataFrame - spark.hadoop.validateOutputSpecsпрацюватиме в усіх API Spark.
samthebest

Чомусь spark.hadoop.validateOutputSpecsне працював для мене на 1.3, але це робить.
Ерік Уокер

1
@samthebest За допомогою save(... , mode=маршруту ви можете перезаписати один набір файлів, додати інший тощо у тому ж контексті Spark. Не spark.hadoop.validateOutputSpecsобмежили б ви лише одним режимом у контексті?
dnlbrky

1
@dnlbrky ОП не просило додавати. Як я вже сказав, справжнє, корисне, але непотрібне. Якщо ОП запитала "як я додаю", тоді можна було б дати цілий спектр відповідей. Але не будемо вникати в це. Також я раджу вам розглянути можливість використання Scala версії DataFrames, оскільки вона має безпеку типу та більше перевірки - наприклад, якби у вас була помилка надпису "перезаписати", ви б не дізналися, поки DAG не буде оцінено - що в роботі Big Data може бути через 2 години !! Якщо ви використовуєте версію Scala, компілятор перевірить все наперед! Досить круто і дуже важливо для Big Data.
samthebest

15

df.write.mode('overwrite').parquet("/output/folder/path")працює, якщо ви хочете перезаписати файл паркету за допомогою python. Це в іскрі 1.6.2. API може бути різним у пізніших версіях


Так, це чудово підходить для моєї вимоги (Databricks)
Nick.McDermaid

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)

Тільки для Spark 1, використання останньої версіїdf.write.mode(SaveMode.Overwrite)
ChikuMiku

3

Ця перевантажена версія функції збереження працює для мене:

yourDF.save (outputPath, org.apache.spark.sql.SaveMode.valueOf ("Перезаписати"))

Наведений вище приклад замінить існуючу папку. Savemode також може приймати ці параметри ( https://spark.apache.org/docs/1.4.0/api/java/org/apache/spark/sql/SaveMode.html ):

Додавання : Режим додавання означає, що при збереженні DataFrame до джерела даних, якщо дані / таблиця вже існує, очікується, що вміст DataFrame буде доданий до існуючих даних.

ErrorIfExists : Режим ErrorIfExists означає, що при збереженні DataFrame до джерела даних, якщо дані вже є, очікується викид.

Ігнорувати : Режим ігнорування означає, що при збереженні DataFrame до джерела даних, якщо дані вже існують, очікується, що операція збереження не збереже вміст DataFrame та не змінить існуючі дані.


1

Якщо ви готові використовувати свій власний формат виводу, ви зможете отримати бажану поведінку і з RDD.

Погляньте на наступні класи: FileOutputFormat , FileOutputCommitter

У форматі виводу файлів у вас є метод з назвою checkOutputSpecs, який перевіряє, чи існує каталог виводу. У FileOutputCommitter у вас є commitJob, який зазвичай передає дані з тимчасового каталогу на остаточне місце.

Я ще не зміг це перевірити (зробив би це, як тільки у мене є кілька вільних хвилин), але теоретично: Якщо я розширюю FileOutputFormat і переосмислюю checkOutputSpecs на метод, який не кидає виняток на каталог вже існує, і відрегулюйте Метод commitJob мого користувальницького вихідного комітента, щоб виконувати будь-яку логіку, яку я хочу (наприклад, переосмислити деякі файли, додати інші), ніж я, можливо, зможу досягти бажаної поведінки і з RDD.

Формат виводу передається в: saveAsNewAPIHadoopFile (яким називається метод saveAsTextFile, а також фактично зберегти файли). І Вивідний комітер налаштований на рівні програми.


Я б не підходив до підкласифікації FileOutputCommitter, якщо ви можете допомогти: це жахливий біт коду. Hadoop 3.0 додає точку плагіна, де FileOutputFormat може приймати різні реалізації рефакторованого суперкласу (PathOutputCommitter). S3 з Netflix запише місце на дерево з розділеним
портом
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.