sc.textfile与sc.wholeTextFiles + flatMapValues

时间:2016-01-22 06:52:52

标签: apache-spark

我有一组日志文件,我想读入RDD。 这些日志文件都是压缩的gzip文件,文件名是 加盖日期。

我一直在使用sc.wholeTextFiles()来读取文件,看起来我一直在遇到Java堆内存问题。为了解决这个问题,我决定在一台机器上针对单个文件运行它作为测试用例。

我从这里获得了文件:

http://dumps.wikimedia.org/other/pagecounts-raw/

以下是文件的大小,包括压缩版本和未压缩版本:

 myuser@fembuntu$ ls -ltr pagecounts-20090505-180000*
 -rw-rw-r-- 1 myuser myuser  65170192 Sep 20  2009 pagecounts-20090505-180000.gz
-rw-rw-r-- 1 myuser myuser 233007266 Jan 22 00:23 pagecounts-20090505-180000.txt

并且机器上的可用内存如下:

myuser@fembuntu:~$ free -tm

       total       used       free     shared    buffers     cached
Mem:    4856       3018       1838        123         27        407
-/+ buffers/cache: 2583       2273
 Swap:  5080        849       4231
Total:  9937       3867       6069

所以我启动了spark-shell,给执行者2G的内存:

$ spark-shell --executor-memory 2G

scala> val pc_loc = "file:///home/myuser/data/pagecounts"
scala> val filename="/pagecounts-20090505-180000.gz"
filename: String = /pagecounts-20090505-180000.gz

在这里,我通过sc.textFile()读取数据并显示前2行:

scala>  var rdd=sc.textFile(pc_loc + filename)
rdd: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[1] at textFile at <console>:31

scala> rdd.take(2)
res0: Array[String] = Array(aa.b Help:Books 1 5263, aa.b Main_Page 1 5416)

工作正常。

这里我使用sc.wholeTextFiles(),并通过flatMapValues()拆分新行以获得一对RDD,其中行是键值对。这些值对应于使用sc.textFile()获得的RDD中的行。关键是文件路径。

scala> val pair_rdd=sc.wholeTextFiles(pc_loc + filename).flatMapValues(y => y.split("\n"))
pair_rdd: org.apache.spark.rdd.RDD[(String, String)] = MapPartitionsRDD[4] at flatMapValues at <console>:31

但是当我执行一个动作时出现堆错误:

scala> pair_rdd.take(2)
16/01/22 01:13:36 ERROR Executor: Exception in task 0.0 in stage 1.0 (TID 1)
java.lang.OutOfMemoryError: Java heap space
at java.nio.HeapCharBuffer.<init>(HeapCharBuffer.java:57)
at java.nio.CharBuffer.allocate(CharBuffer.java:335)
at java.nio.charset.CharsetDecoder.decode(CharsetDecoder.java:795)
at org.apache.hadoop.io.Text.decode(Text.java:412)

任何人都可以解释这里发生了什么吗?为什么flatMapValues调用拆分线似乎会破坏Java堆内存使用量,从而导致堆错误?

2 个答案:

答案 0 :(得分:8)

您遇到的问题并非真正针对具有textFile方案的wholeTextFiles vs flatMapValues。看起来您的程序甚至没有达到数据平坦的程度,而且我很确定当您拨打count而不是mapValues时,您会收到同样的异常。

实际上只需要创建大型对象。请记住,wholeTextFiles必须立即读取文件的完整内容,不能将其部分溢出到磁盘或部分垃圾回收。虽然200MB左右不是特别令人印象深刻,但是单个对象处理的却相当多。此外,它必须驻留在一台机器上,这意味着分配负载更加困难。

wholeTextFiles不同,textFile在此特定情况下提供更高的粒度。单个对象必须处理的数据少得多,如果不再需要,可以有效地进行垃圾回收。

忽略对象的大小,看起来你在本地模式下使用Spark。这意味着一切都由一个JVM处理。由于堆由所有线程共享,这意味着可用于实际处理的内存量可能低于您的预期。

最后你应该记住,只有一部分可用内存是为堆保留的。请参阅Garbage Collector ErgonomicsHow is the default java heap size determined?。如果必须处理大型对象,则始终可以使用-Xms / -Xmx Java选项覆盖默认的初始和最大堆大小。

答案 1 :(得分:2)

在第一种情况下,当你调用动作时,Spark会发现它需要返回第二行。由于RDD被懒惰地评估,它只读取它需要返回的数据。

在第二种情况下,当您调用操作时,将调用两个操作,读取文件并对整个文件应用flatMapValues。因此,在这种情况下,需要读取和处理整个文件,然后返回第二行。

至少可以解释为什么在第二种情况下需要更多内存。