在Spark

时间:2016-02-29 16:03:30

标签: scala apache-spark

在Spark中,我有一个包含数百万个本地文件路径的RDD(我们有一个共享文件系统,因此它们显示在本地)。在Scala中,我如何创建一个由每个文件中的所有行组成的RDD?

我尝试过这样的事情:

paths.flatMap(path => sc.textFile(path))

但那没用。我也试过这样的事情:

paths.flatMap(path => 
    scala.io.Source.fromInputStream(new java.io.FileInputStream(path)).getLines
)

在本地工作时有效但在多台机器上运行时却没有。我最终得到了这个错误:

java.nio.charset.MalformedInputException: Input length = 1
    at java.nio.charset.CoderResult.throwException(CoderResult.java:277)

任何指针都将不胜感激

(到目前为止,大多数解决方案都涉及将文件列表一次性传递给sc.textFile,这是不可能的,因为列表可能非常大。现在一个典型的用例会产生20M路径,而不是适合单个Java String)。

2 个答案:

答案 0 :(得分:2)

如果他们在一个目录中,那么最好阅读整个目录

 sc.textFile("/path/to/directory/") 

将所有文件合并为一个RDD,注意mem约束。或者,您可以尝试地图然后减少:

paths.map(path => sc.textFile(path)).reduce(_.union(_))

甚至更好,因为零323建议:

SparkContext.union(paths.map(...))

答案 1 :(得分:2)

此:

paths.flatMap(path => sc.textFile(path))

根本无法编译而不提工作,因为RDD不是TraversableOnce

直接读取文件时看到的错误(java.nio.charset.MalformedInputException)与Spark无关,并且在文件编码错误时抛出。引用MalformedInputException documentation

  

当输入字节序列对于给定字符集不合法,或者输入字符序列不是合法的16位Unicode序列时,抛出检查异常。你可以解决它,为fromInputStream方法提供编解码器:

def fromInputStream(is: InputStream)(implicit codec: Codec)

并将Codec.onMalformedInput与所需的CodingErrorAction

一起使用

请参阅示例Source.fromInputStream exception handling during reading lines

此外,当您直接读取数据时,您应该处理IO异常,例如通过使用Try包装读取块。

Spark本身支持读取完整的目录树。没有理由传递单个文件,只传递顶级目录列表并使用mapreduce.input.fileinputformat.input.dir.recursive配置的正确设置。也可以将多个根目录作为逗号分隔的字符串传递:

sc.textFile("/foo,/bar")

您还可以使用通配符来读取任意文件和目录列表:

sc.textFile(Seq(
  "/foo/bar/*",
  "/bar/*/*.txt"
).mkString(","))

最后,由于计算输入分割的方式,读取大量小文件效率很低。而不是使用textFiles,您应该考虑使用CombineFileInputFormat的子类进行阅读,例如:

sc.hadoopFile(
  paths,
  classOf[CombineTextInputFormat],
  classOf[LongWritable], classOf[Text]
).map(_._2.toString)

最后,您可以{@ 1}} @GameOfThrows建议的多个RDD,但不应在没有检查点to avoid issues with long lineages的情况下迭代完成。请改用union并控制分区数。