Spark mapPartition输出对象大小大于预期

时间:2017-02-06 22:04:50

标签: apache-spark

我将mapPartitions的输出存储在ListBuffer中,并将其迭代器作为输出公开。输出是Long元组(Tuple2)的列表。当我使用Spark的SizeEstimator.estimate方法检查对象的大小时,每个记录/元组对象出现80个字节(通过" ListBuffer对象的大小/#记录"计算)。我认为这对于长类型的Tuple2对象来说太大了(两个8字节长+一些对象开销内存)。任何想法为什么会这样,以及如何减少输出捕获的内存?我相信我错过了一些明显的东西。

此外,这些ListBuffer对象对于内存来说太大了,导致内存和磁盘溢出导致性能下降。关于如何只是简单地编写mapPartitions的输出而不将整个输出存储为内存中对象的任何想法。 mapPartitions的每个输入记录都可以生成0个或多个输出记录,所以我想我不能使用" rdd.map"函数迭代器。即使这对我的事业有帮助,我也不确定。

以下是代码段:

var outputRDD = sortedRDD.mapPartitionsWithIndex((partitionNo,p) => { 
      var outputList = ListBuffer[(Long,Long)]() 
      var inputCnt: Long = 0; 
      var outputCnt: Long = 0; 
  while (p.hasNext) { 
      inputCnt = inputCnt + 1; 
    val tpl = p.next() 
    var partitionKey = "" 
    try{ 
      partitionKey = tpl._1.split(keyDelimiter)(0)                          //Partition key 
    }catch{ 
      case aob : ArrayIndexOutOfBoundsException => { 
        println("segmentKey:"+partitionKey); 
      } 
    }   
    val value = tpl._2 
    var xs: Array[Any] = value.toSeq.toArray; 
    //value.copyToArray(xs); 

    val xs_string : Array[String] = new Array[String](value.size); 
    for(i <- 0 to value.size-1){ 
      xs_string(i) = xs(i) match { case None => "" 
                                   case null => "" 
                                   case _ =>  xs(i).toString() 
                                 } 
    } 

    val outputTuples = windowObject.process(partitionKey, xs_string); 

            if(outputTuples != null){ 
                    for (i <- 0 until outputTuples.size()) { 
                            val outputRecord = outputTuples.get(i) 
                            if (outputRecord != null) { 
                                    outputList += ((outputRecord.getProfileID1 , outputRecord.getProfileID2)) 
                                    outputCnt = outputCnt +1; 
                            } 
                    } 
            }   
  } 

      if(debugFlag.equals("DEBUG")){    
            logger.info("partitionNo:"+ partitionNo + ", input #: "+ inputCnt +", output #: "+ outputCnt+", outputList object size:" + SizeEstimator.estimate(outputList)); 
      } 

      outputList.iterator 

}, false)

1 个答案:

答案 0 :(得分:0)

ListBuffer通常比ArrayBuffer占用更多空格,因为它会为列表中的每个项目创建一个列表节点。但是,由于对象开销,Tuple2 [Long,Long]通常占用72个字节(至少在我的笔记本电脑中)。因此,在使用ListBuffer替换ArrayBuffer后,您可能无法节省太多。

对于第二个问题,您可以使用RDD.flatMap将记录映射到序列结果。它可以避免将整个分区的结果放入outputList