如何在Spark中覆盖输出目录


107

我有一个Spark Streaming应用程序,每分钟生成一个数据集。我需要保存/覆盖已处理数据的结果。

当我尝试覆盖数据集org.apache.hadoop.mapred.FileAlreadyExistsException时,将停止执行。

我设置了Spark属性set("spark.files.overwrite","true"),但是没有运气。

如何从Spark覆盖或预删除文件?


1
是的,它不是很糟糕,我认为这是对0.9.0的回归。请接受我的回答:)
samthebest 2015年

set("spark.files.overwrite","true")仅适用于spark.addFile()
从头到尾

Answers:


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标志的spark-submit脚本来设置conf设置。

警告(旧版本):根据@piggybox的说法,Spark中存在一个错误,该错误仅会覆盖写入其part-文件所需的文件,而其他文件则不会被删除。


29
Spark 1.4df.write.mode(SaveMode.Overwrite).parquet(path)
夏潘

对于Spark SQL,您可以选择为Core Spark定义SaveMode,而您不需要这样的选项。真的想为saveAsTextFile和其他转换提供某种功能
Murtaza Kanchwala 2015年

3
一个隐藏的问题:与@pzecevic通过HDFS清除整个文件夹的解决方案相比,在这种方法中,Spark只会覆盖输出文件夹中具有相同文件名的零件文件。这在大多数情况下都是可行的,但是如果文件夹中还有其他内容,例如来自另一个Spark / Hadoop作业的多余零件文件,则不会覆盖这些文件。
储钱罐2015年

6
您还可以使用df.write.mode(mode: String).parquet(path)Where模式:字符串可以是:“ overwrite”,“ append”,“ ignore”,“ error”。
黑麦

1
@avocado Yup这么认为,Spark API在每个发行版上都会变得越来越糟: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个分区/文件,然后用仅包含6个分区的DataFrame覆盖了文件夹,则结果文件夹将具有6个分区/文件。

有关模式选项的更多信息,请参见Spark SQL文档


2
确实,非常有用,谢谢,但是DataFrame特定的解决方案- spark.hadoop.validateOutputSpecs可以在所有Spark API中使用。
samthebest

出于某种原因,它spark.hadoop.validateOutputSpecs在1.3上对我不起作用,但确实如此。
埃里克·沃克

1
@samthebest使用该save(... , mode=路由,您可以在同一Spark上下文中覆盖一组文件,追加另一组文件,等等。不会spark.hadoop.validateOutputSpecs将您限制为每个上下文仅一种模式吗?
dnlbrky 2015年

1
@dnlbrky OP没有要求追加。正如我所说,是真实的,有用的,但不必要的。如果OP询问“我该如何追加”,则可以给出所有答案。但是,我们不要讨论这一点。另外,我建议您考虑使用DataFrames的Scala版本,因为它具有类型安全性和更多检查功能-例如,如果您在“覆盖”中有错别字,则在评估DAG之前不会发现它-在大数据作业中可以2小时后!如果您使用Scala版本,则编译器将预先检查所有内容!很酷,对于大数据非常重要。
samthebest 2015年

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(“ Overwrite”))

上面的示例将覆盖现有文件夹。保存模式也可以采用以下参数(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获得所需的行为。

看一下以下类: FileOutputFormatFileOutputCommitter

在文件输出格式中,您有一个名为checkOutputSpecs的方法,该方法用于检查输出目录是否存在。在FileOutputCommitter中,您具有commitJob,通常将数据从临时目录传输到其最终位置。

我还无法验证(只要有空闲时间,就可以验证),但从理论上讲:如果我扩展FileOutputFormat并将checkOutputSpecs重写为一个不存在已在目录上引发异常的方法,并调整我的自定义输出提交程序的commitJob方法可以执行我想要的任何逻辑(例如,覆盖某些文件,附加其他文件),而我可能也可以通过RDD实现所需的行为。

输出格式将传递到:saveAsNewAPIHadoopFile(这也是实际保存文件时调用的saveAsTextFile方法)。并且在应用程序级别配置了输出提交者。


如果可以帮助的话,我会避免将FileOutputCommitter子类化:这是一段令人恐惧的代码。Hadoop 3.0添加了一个插件点,其中FileOutputFormat可以采用重构超类(PathOutputCommitter)的不同实现。来自Netflix的S3将就地写入分区树中,仅在作业提交时才进行冲突解决(失败,删除,添加),并且仅在更新的分区中执行
stevel
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.