MapOutputTracker serializeMapStatuses的Spark内存不足错误

时间:2017-09-30 23:59:09

标签: apache-spark emr

我有一个火花工作,在第0阶段有数十万(300,000任务和更多)任务,然后在洗牌期间,驱动程序端会抛出以下异常:

util.Utils: Suppressing exception in finally: null 
java.lang.OutOfMemoryError at 
java.io.ByteArrayOutputStream.hugeCapacity(ByteArrayOutputStream.java:123) at 
java.io.ByteArrayOutputStream.grow(ByteArrayOutputStream.java:117) at 
java.io.ByteArrayOutputStream.ensureCapacity(ByteArrayOutputStream.java:93) at 
java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:153) at 
java.util.zip.DeflaterOutputStream.deflate(DeflaterOutputStream.java:253) at 
java.util.zip.DeflaterOutputStream.write(DeflaterOutputStream.java:211) at 
java.util.zip.GZIPOutputStream.write(GZIPOutputStream.java:145) at 
java.io.ObjectOutputStream$BlockDataOutputStream.writeBlockHeader(ObjectOutputStream.java:1894) at 
java.io.ObjectOutputStream$BlockDataOutputStream.drain(ObjectOutputStream.java:1875) at 
java.io.ObjectOutputStream$BlockDataOutputStream.flush(ObjectOutputStream.java:1822) at 
java.io.ObjectOutputStream.flush(ObjectOutputStream.java:719) at 
java.io.ObjectOutputStream.close(ObjectOutputStream.java:740) at 
org.apache.spark.MapOutputTracker$$anonfun$serializeMapStatuses$2.apply$mcV$sp(MapOutputTracker.scala:618) at 
org.apache.spark.util.Utils$.tryWithSafeFinally(Utils.scala:1319) at 
org.apache.spark.MapOutputTracker$.serializeMapStatuses(MapOutputTracker.scala:617) at 
org.apache.spark.MapOutputTrackerMaster.getSerializedMapOutputStatuses(MapOutputTracker.scala:560) at 
org.apache.spark.MapOutputTrackerMaster$MessageLoop.run(MapOutputTracker.scala:349) at 
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at 
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745)

我检查了ByteArrayOutputStream代码,当数组大小大于INTEGER.MAX(大约2G)时,它会抛出内存不足错误。这意味着地图状态序列化结果应小于2G。

我还检查了MapOutputTracker代码,这个地图状态大小与任务大小和后续阶段任务大小有关。

我想知道是否有人遇到这个问题,你是如何解决这个问题的。我的理解是我们只能减少任务的大小,但我的任务只能停留,因为较少的分区会延迟计算。

1 个答案:

答案 0 :(得分:0)

这可能是由于一个shuffle中超过2GB内存的单个块造成的。这通常意味着您的操作需要更大的并行性,这将减小任何单个块的大小 - 希望低于2GB限制(这是非常高的。)

没有Spark shuffle块可以大于2 GB

Spark使用ByteBuffer作为存储块的抽象。

  val buf =ByteBuffer.allocate(length.toInt)

  ByteBuffer is limited by Integer.MAX_IZE(2GB)

提高并行度

1)在调用导致此错误的操作之前重新分区数据,如下所示:

DataFrame.repartition(400)
RDD.repartition(400)

2)将分区数作为最后一个参数(支持的位置)传递给操作:

 import org.apache.spark.rdd.PairRDDFunctions
 RDD.groupByKey(numPartitions: Int) RDD.join(other: RDD, numPartitions:Int)

3)通过SparkConf设置默认并行度(分区),如下所示(在Databricks Cloud中不支持):

// create the SparkConf used to create the SparkContext val conf = new SparkConf()
// set the parallelism/partitions conf.set("spark.default.parallelism", 400)

// create the SparkContext with the conf val sc = new SparkContext(conf)

// check the parallelism/partitions sc.defaultParallelism

4)通过SQL设置SQL分区,如下所示(默认为200):

SET spark.sql.shuffle.partitions = 400;

为什么2GB?

由于Java Integers的限制,存在此限制:2 ^ 31 == 2,147,483,647~ = 2GB。

Spark的shuffle机制目前使用Java ByteArrays通过网络传输数据。

将来通过扩展Sp​​ark的shuffle来使用Longs使用更大的ByteArrays,将ByteArrays链接在一起,或者两者兼而有之,这将会得到增强。

https://forums.databricks.com/questions/1140/im-seeing-an-outofmemoryerror-requested-array-size.html