Für diejenigen, die mit einem größeren Datensatz arbeiten :
rdd.collect()
sollte in diesem Fall nicht verwendet werden , da alle Daten Array
im Treiber erfasst werden. Dies ist der einfachste Weg, um aus dem Speicher herauszukommen.
rdd.coalesce(1).saveAsTextFile()
sollte auch nicht verwendet werden, da die Parallelität von Upstream-Stufen verloren geht und auf einem einzelnen Knoten ausgeführt wird, von dem aus Daten gespeichert werden.
rdd.coalesce(1, shuffle = true).saveAsTextFile()
ist die beste einfache Option, da die Verarbeitung von Upstream-Aufgaben parallel bleibt und dann nur das Mischen zu einem Knoten durchgeführt wird ( rdd.repartition(1).saveAsTextFile()
ist ein genaues Synonym).
rdd.saveAsSingleTextFile()
Wie unten angegeben, können Sie die Festplatte zusätzlich in einer einzelnen Datei mit einem bestimmten Namen speichern, wobei die Parallelitätseigenschaften von beibehalten werden rdd.coalesce(1, shuffle = true).saveAsTextFile()
.
Etwas, das unpraktisch sein kann, rdd.coalesce(1, shuffle = true).saveAsTextFile("path/to/file.txt")
ist, dass es tatsächlich eine Datei erzeugt, deren Pfad ist path/to/file.txt/part-00000
und nicht path/to/file.txt
.
Die folgende Lösung erzeugt rdd.saveAsSingleTextFile("path/to/file.txt")
tatsächlich eine Datei mit dem Pfad path/to/file.txt
:
package com.whatever.package
import org.apache.spark.rdd.RDD
import org.apache.hadoop.fs.{FileSystem, FileUtil, Path}
import org.apache.hadoop.io.compress.CompressionCodec
object SparkHelper {
implicit class RDDExtensions(val rdd: RDD[String]) extends AnyVal {
def saveAsSingleTextFile(path: String): Unit =
saveAsSingleTextFileInternal(path, None)
def saveAsSingleTextFile(path: String, codec: Class[_ <: CompressionCodec]): Unit =
saveAsSingleTextFileInternal(path, Some(codec))
private def saveAsSingleTextFileInternal(
path: String, codec: Option[Class[_ <: CompressionCodec]]
): Unit = {
val hdfs = FileSystem.get(rdd.sparkContext.hadoopConfiguration)
hdfs.delete(new Path(s"$path.tmp"), true)
codec match {
case Some(codec) => rdd.saveAsTextFile(s"$path.tmp", codec)
case None => rdd.saveAsTextFile(s"$path.tmp")
}
hdfs.delete(new Path(path), true)
FileUtil.copyMerge(
hdfs, new Path(s"$path.tmp"),
hdfs, new Path(path),
true, rdd.sparkContext.hadoopConfiguration, null
)
hdfs.delete(new Path(s"$path.tmp"), true)
}
}
}
die so verwendet werden kann:
import com.whatever.package.SparkHelper.RDDExtensions
rdd.saveAsSingleTextFile("path/to/file.txt")
import org.apache.hadoop.io.compress.GzipCodec
rdd.saveAsSingleTextFile("path/to/file.txt.gz", classOf[GzipCodec])
Dieser Ausschnitt:
Speichert zuerst die Festplatte mit rdd.saveAsTextFile("path/to/file.txt")
in einem temporären Ordner, path/to/file.txt.tmp
als ob wir keine Daten in einer Datei speichern wollten (wodurch die Verarbeitung von Upstream-Aufgaben parallel bleibt).
Und dann fahren wir nur mit der Hadoop-Dateisystem-API mit dem Zusammenführen ( FileUtil.copyMerge()
) der verschiedenen Ausgabedateien fort, um unsere endgültige Ausgabedatei zu erstellen path/to/file.txt
.