对于使用较大数据集的用户:
rdd.collect()
在这种情况下不应该使用它,因为它将在驱动程序中以的形式收集所有数据Array
,这是获取内存的最简单方法。
rdd.coalesce(1).saveAsTextFile()
也不应使用上游级的并行性,因为上游级的并行性将丢失,无法在存储数据的单个节点上执行。
rdd.coalesce(1, shuffle = true).saveAsTextFile()
是最好的简单选择,因为它将保持并行处理上游任务,然后仅对一个节点执行洗牌(这rdd.repartition(1).saveAsTextFile()
是确切的同义词)。
rdd.saveAsSingleTextFile()
如下提供的如下所示,它还允许将rdd存储在具有指定名称的一个文件中,同时保留的并行性rdd.coalesce(1, shuffle = true).saveAsTextFile()
。
可能不方便的rdd.coalesce(1, shuffle = true).saveAsTextFile("path/to/file.txt")
是,它实际上生成的文件的路径为path/to/file.txt/part-00000
and而不是path/to/file.txt
。
以下解决方案rdd.saveAsSingleTextFile("path/to/file.txt")
实际上将产生一个路径为的文件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)
}
}
}
可以这样使用:
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])
此代码段: