Memory management 已超出Spark Executor LostFailure内存

Memory management 已超出Spark Executor LostFailure内存,memory-management,apache-spark,yarn,coalesce,Memory Management,Apache Spark,Yarn,Coalesce,几天来,我一直在尝试让spark作业运行到完成,我终于能够让它完成,但仍有大量失败的任务,其中执行者被以下消息杀死: ExecutorLostFailure(执行器77因某个正在运行的任务而退出)原因:容器因超出内存限制而被Thread杀死。使用44.9 GB物理内存中的45.1 GB。考虑提升火花.纱线.执行器.内存开销 以下是我要传递给集群的属性: [ { "classification": "spark-defaults", "properties"

几天来,我一直在尝试让spark作业运行到完成,我终于能够让它完成,但仍有大量失败的任务,其中执行者被以下消息杀死:

ExecutorLostFailure(执行器77因某个正在运行的任务而退出)原因:容器因超出内存限制而被Thread杀死。使用44.9 GB物理内存中的45.1 GB。考虑提升火花.纱线.执行器.内存开销

以下是我要传递给集群的属性:

[
    {
        "classification": "spark-defaults",
        "properties": {
            "spark.executor.memory": "41000m",
            "spark.driver.memory": "8000m",
            "spark.executor.cores": "6",
            "spark.shuffle.service.enabled": "true",
            "spark.executor.instances": "98",
            "spark.yarn.executor.memoryOverhead": "5000"
        }
    }
]
该集群由20台机器组成,每台机器具有32个内核和240G内存。我应该继续把记忆提高到一个更高的水平,还是有一个更深层次的问题。这次的错误似乎发生在将结果数据写入S3之前从5000个分区合并到500个分区的过程中。我猜合并导致了混乱,因为集群的内存已经不足,所以它把它推得太远了

工作流程如下:

  • 将拼花地板文件从s3加载到数据帧中
  • 提取一组使用sql查询对dataframe进行数据分组的唯一键
  • 将数据帧转换为JavaRDD并应用几个映射函数
  • MapToPair数据
  • combineByKey使用下面的方法,实质上是通过键将单个对象合并到对象数组中

    combineByKey(新函数、添加函数、合并函数、新HashPartitioner(5000)、false、null)

  • 更多地图

  • 对于多个唯一键中的每一个,过滤rdd以仅获得具有该键的元组,然后在合并后将这些子集中的每一个持久化到磁盘
  • 另一个问题是如何推导出上面的44.9数字。我估计最大内存将是executor memory+memoryOverhead,即46G而不是44.9G

    任何帮助都将不胜感激,
    内森

    根据我的经验,这表明了一个更深层次的问题,从你的帖子中,我看到了几个陷阱

    首先,您可能需要查看分区大小,因为OOM很容易由
    combineByKey
    操作期间创建的数据倾斜引起。也许有些钥匙很常用

    如果没有,我将查看
    coalesce
    函数调用。您尚未发布代码,因此我只能猜测正在生成的DAG,但我知道
    coalesce
    函数以及在同一写入阶段执行的其他操作

    Spark分阶段执行,从您的解释中我可以看出,您在
    写入之前调用
    合并
    ,因此取决于进入最后阶段的分区数量以及在此阶段中完成的转换,实际上,您操作的分区可能少于所需的分区,从而导致OOM异常

    用文字来解释有点复杂,但我将尝试给出一个简单的例子,说明可能发生的情况

    想象一下,在一个简单的场景中,您读取一个包含say
    (Int,Double)
    的键值对的文件,然后对所有值应用一些函数,比如say
    round
    。然后,您希望将输出写回单个文件,因此调用
    coalesce(1)
    ,然后调用
    write
    。代码如下所示:

    val df = sqlContext.read.parquet("/path/to/my/file/")
    df.map{case(key: Int, value: Double) => (key, round(value)}
      .toDF()
      .coalesce(1)
      .write
      .parquet("/my/output/path/")
    
    现在有人可能会认为
    map
    操作是在整个集群上并行执行的,但是如果您注意spark ui,您会注意到该任务并没有分布在整个集群中。由于
    coalesce(1)
    ,Spark知道一切都需要在单个分区中结束,因此它只需在运行过程中应用
    map
    函数将所有数据收集到一个分区中。正如您可能想象的那样,这很容易在OOM异常中以更复杂的转换结束


    我希望这能给你一些关于去哪里找的建议。祝你好运:)

    Nathan,你的答案已经贴出来了,你没看到吗?:)太棒了,你提到了分区!