在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)。
答案 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
并控制分区数。