Pour spark sql, comment devrions-nous récupérer les données d’un dossier dans HDFS, y apporter des modifications et enregistrer les données mises à jour dans le même dossier dans HDFS via le mode d’enregistrement par écrasement sans l’exception FileNotFoundException?
import org.Apache.spark.sql.{SparkSession,SaveMode}
import org.Apache.spark.SparkConf
val sparkConf: SparkConf = new SparkConf()
val sparkSession = SparkSession.builder.config(sparkConf).getOrCreate()
val df = sparkSession.read.parquet("hdfs://xxx.xxx.xxx.xxx:xx/test/d=2017-03-20")
val newDF = df.select("a","b","c")
newDF.write.mode(SaveMode.Overwrite)
.parquet("hdfs://xxx.xxx.xxx.xxx:xx/test/d=2017-03-20") // doesn't work
newDF.write.mode(SaveMode.Overwrite)
.parquet("hdfs://xxx.xxx.xxx.xxx:xx/test/d=2017-03-21") // works
FileNotFoundException se produit lorsque nous lisons des données à partir du répertoire hdfs "d = 2017-03-20" et que nous sauvegardons (SaveMode.Overwrite) des données mises à jour dans le même répertoire hdfs "d = 2017-03-20".
Caused by: org.Apache.spark.SparkException: Task failed while writing rows
at org.Apache.spark.sql.execution.datasources.FileFormatWriter$.org$Apache$spark$sql$execution$datasources$FileFormatWriter$$executeTask(FileFormatWriter.scala:204)
at org.Apache.spark.sql.execution.datasources.FileFormatWriter$$anonfun$write$1$$anonfun$3.apply(FileFormatWriter.scala:129)
at org.Apache.spark.sql.execution.datasources.FileFormatWriter$$anonfun$write$1$$anonfun$3.apply(FileFormatWriter.scala:128)
at org.Apache.spark.scheduler.ResultTask.runTask(ResultTask.scala:87)
at org.Apache.spark.scheduler.Task.run(Task.scala:99)
at org.Apache.spark.executor.Executor$TaskRunner.run(Executor.scala:282)
at Java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.Java:1142)
at Java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.Java:617)
at Java.lang.Thread.run(Thread.Java:745)
Caused by: Java.io.FileNotFoundException: File does not exist: hdfs://xxx.xxx.xxx.xxx:xx/test/d=2017-03-20/part-05020-35ea100f-829e-43d9-9003061-1788904de770.snappy.parquet
It is possible the underlying files have been updated. You can explicitly invalidate the cache in Spark by running 'REFRESH TABLE tableName' command in SQL or by recreating the Dataset/DataFrame involved.
at org.Apache.spark.sql.execution.datasources.FileScanRDD$$anon$1.nextIterator(FileScanRDD.scala:157)
at org.Apache.spark.sql.execution.datasources.FileScanRDD$$anon$1.hasNext(FileScanRDD.scala:102)
at org.Apache.spark.sql.catalyst.expressions.GeneratedClass$GeneratedIterator.scan_nextBatch$(Unknown Source)
at org.Apache.spark.sql.catalyst.expressions.GeneratedClass$GeneratedIterator.processNext(Unknown Source)
at org.Apache.spark.sql.execution.BufferedRowIterator.hasNext(BufferedRowIterator.Java:43)
at org.Apache.spark.sql.execution.WholeStageCodegenExec$$anonfun$8$$anon$1.hasNext(WholeStageCodegenExec.scala:377)
at org.Apache.spark.sql.execution.datasources.FileFormatWriter$SingleDirectoryWriteTask.execute(FileFormatWriter.scala:243)
at org.Apache.spark.sql.execution.datasources.FileFormatWriter$$anonfun$org$Apache$spark$sql$execution$datasources$FileFormatWriter$$executeTask$3.apply(FileFormatWriter.scala:190)
at org.Apache.spark.sql.execution.datasources.FileFormatWriter$$anonfun$org$Apache$spark$sql$execution$datasources$FileFormatWriter$$executeTask$3.apply(FileFormatWriter.scala:188)
at org.Apache.spark.util.Utils$.tryWithSafeFinallyAndFailureCallbacks(Utils.scala:1341)
at org.Apache.spark.sql.execution.datasources.FileFormatWriter$.org$Apache$spark$sql$execution$datasources$FileFormatWriter$$executeTask(FileFormatWriter.scala:193)
... 8 more
Les tentatives suivantes ont toujours la même erreur. Comment puis-je résoudre ce problème en utilisant spark sql? Je vous remercie!
val hdfsDirPath = "hdfs://xxx.xxx.xxx.xxx:xx/test/d=2017-03-20"
val df= sparkSession.read.parquet(hdfsDirPath)
val newdf = df
newdf.write.mode(SaveMode.Overwrite).parquet(hdfsDirPath)
ou
val df= sparkSession.read.parquet(hdfsDirPath)
df.createOrReplaceTempView("orgtable")
sparkSession.sql("SELECT * from orgtable").createOrReplaceTempView("tmptable")
sparkSession.sql("TRUNCATE TABLE orgtable")
sparkSession.sql("INSERT INTO orgtable SELECT * FROM tmptable")
val newdf = sparkSession.sql("SELECT * FROM orgtable")
newdf.write.mode(SaveMode.Overwrite).parquet(hdfsDirPath)
ou
val df= sparkSession.read.parquet(hdfsDirPath)
df.createOrReplaceTempView("orgtable")
sparkSession.sql("SELECT * from orgtable").createOrReplaceTempView("tmptable")
sparkSession.sql("REFRESH TABLE orgtable")
sparkSession.sql("ALTER VIEW tmptable RENAME TO orgtable")
val newdf = sparkSession.sql("SELECT * FROM orgtable")
newdf.write.mode(SaveMode.Overwrite).parquet(hdfsDirPath)
J'ai résolu ceci, d'abord j'écris mon Dataframe dans un répertoire temporaire, supprime la source que je lis et renomme le répertoire temporaire en nom de source. QAQ
Pourquoi ne le cachez-vous pas après l'avoir lu? L'enregistrer dans un autre répertoire de fichiers, puis le déplacer peut nécessiter des autorisations supplémentaires. J'ai aussi forcé une action, comme un show ().
val myDF = spark.read.format("csv")
.option("header", "false")
.option("delimiter", ",")
.load("/directory/tofile/")
myDF.cache()
myDF.show(2)
val dfOut = df.filter(r => r.getAs[Long]("dsctimestamp") > (System.currentTimeMillis() - 1800000))
Dans la ligne de code ci-dessus, df
avait une partition Hadoop sous-jacente. Une fois cette transformation effectuée (c.-à-d. En dfOut
), je n’ai pas trouvé de moyen de supprimer, renommer ou écraser la partition sous-jacente tant que dfOut
n’a pas été nettoyé.
Ma solution consistait à conserver l'ancienne partition, à créer une nouvelle partition pour dfOut
, à marquer la nouvelle partition comme étant actuelle, puis à supprimer l'ancienne partition à un moment donné, après que dfOut
eut été nettoyé.
Peut-être pas une solution idéale. J'aimerais apprendre une façon moins tortueuse de régler ce problème. Mais ça marche.