Spark缓存的RDD计算了n次

时间:2019-02-14 14:33:49

标签: scala performance apache-spark yarn

Spark应用程序遇到问题。这是我的代码的简化版本:

def main(args: Array[String]) {
    // Initializing spark context
    val sc = new SparkContext()
    val nbExecutors = sc.getConf.getInt("spark.executor.instances", 3)
    System.setProperty("spark.sql.shuffle.partitions", nbExecutors.toString)

    // Getting files from TGZ archives
    val archivesRDD: RDD[(String,PortableDataStream)] = utils.getFilesFromHDFSDirectory("/my/dir/*.tar.gz") // This returns an RDD of tuples containing (filename, inpustream)
    val filesRDD: RDD[String] = archivesRDD.flatMap(tgzStream => {
        logger.debug("Getting files from archive : "+tgzStream._1)
        utils.getFilesFromTgzStream(tgzStream._2)
    })

    // We run the same process with 3 different "modes"
    val modes = Seq("mode1", "mode2", "mode3")

    // We cache the RDD before
    val nb = filesRDD.cache().count()
    logger.debug($nb + " files as input")

    modes.map(mode => {
        logger.debug("Processing files with mode : " + mode)
        myProcessor.process(mode, filesRDD)
    })

    filesRDD.unpersist() // I tried with or without this

    [...]
}

生成的日志为(例如输入3个档案):

  

从存档中获取文件:a

     

从存档中获取文件:b

     

从存档中获取文件:c

     

3个文件作为输入

     

以以下模式处理文件:mode1

     

从存档中获取文件:a

     

从存档中获取文件:b

     

从存档中获取文件:c

     

以以下模式处理文件:mode2

     

从存档中获取文件:a

     

从存档中获取文件:b

     

从存档中获取文件:c

     

以以下模式处理文件:mode3

     

从存档中获取文件:a

     

从存档中获取文件:b

     

从存档中获取文件:c

我的Spark配置:

  • 版本:1.6.2
  • 执行器:20 x 2CPU x 8Go RAM
  • 每个执行者的纱线开销内存:800Mo
  • 驱动程序:1CPU x 8Go RAM

我从这些日志中了解到的是,文件提取是一次读取的4倍!显然,这导致我遇到了堆空间问题和性能泄漏...

我做错什么了吗?

编辑:我也尝试使用modes.foreach(...)代替地图,但没有做任何更改...

2 个答案:

答案 0 :(得分:0)

您是否尝试过将modes.map结果传递给List构造函数(即List(modes.map{ /*...*/}))?有时(我不确定何时),Scala集合会延迟评估映射,因此,如果直到spark删除缓存后才对这些评估进行评估,就必须重新计算。

答案 1 :(得分:0)

好的,经过大量测试,我终于解决了这个问题。实际上有两个问题:

  1. 我低估了输入数据的大小:如果RDD太大而无法完全存储在60%的RDD中,Spark的cachepersist函数的效率会降低。总内存,我知道,但以为我的输入数据不是很大,但实际上我的RDD是80GB。但是我的60%的内存(即160GB)仍然超过80GB,那么怎么了?在第2个问题中回答...

  2. 我的分区太大:在我的代码中的某个地方,RDD的分区数设置为100,所以我有100个分区,每个分区1.6GB。问题是我的数据是由数十个Meg组成的字符串组成的,因此我的分区未满,实际上10GB的已用内存仅包含7GB或8GB的真实数据。

要解决这些问题,我不得不使用persist(StorageLevel.MEMORY_SER),这会增加计算时间,但会大大减少内存使用量(according to this benchmark),并将分区号设置为1000(根据Spark文档,建议分区为〜)。 128MB)