Apache Spark OutOfMemoryError(HeapSpace)

时间:2017-04-26 15:02:53

标签: apache-spark pyspark apache-spark-sql pyspark-sql

我有一个大约5M行x20列的数据集,包含groupID和rowID。我的目标是检查(某些)列是否包含组内缺少(空)值的固定分数(例如,50%)。如果找到,则该组的整个列设置为missing(null)。

df = spark.read.parquet('path/to/parquet/')
check_columns = {'col1': ..., 'col2': ..., ...}  # currently len(check_columns) = 8

for col, _ in check_columns.items():
    total = (df
             .groupBy('groupID').count()
             .toDF('groupID', 'n_total')
             )

    missing = (df
               .where(F.col(col).isNull())
               .groupBy('groupID').count()
               .toDF('groupID', 'n_missing')
               )
    # count_missing = count_missing.persist()  # PERSIST TRY 1
    # print('col {} found {} missing'.format(col, missing.count()))  # missing.count() is b/w 1k-5k

    poor_df = (total
               .join(missing, 'groupID')
               .withColumn('freq', F.col('n_missing') / F.col('n_total'))
               .where(F.col('freq') > 0.5)
               .select('groupID')
               .toDF('poor_groupID')
               )

    df = (df
          .join(poor_df, df['groupID'] == poor_df['poor_groupID'], 'left_outer')
          .withColumn(col, (F.when(F.col('poor_groupID').isNotNull(), None)
                            .otherwise(df[col])
                            )
                    )
        .select(df.columns)
        )

    stats = (missing
             .withColumnRenamed('n_missing', 'cnt')
             .collect()  # FAIL 1
             )

    # df = df.persist()  # PERSIST TRY 2

print(df.count())  # FAIL 2

我最初分配了1G spark.driver.memory和4G spark.executor.memory,最终将spark.driver.memory增加到10G。

问题(S): 在第一次迭代中,循环像魅力一样运行,但是到最后, 在第6或第7次迭代中,我看到我的CPU利用率下降(使用1 而不是6个核心)。与此同时,一次迭代的执行时间 显着增加。 在某些时候,我得到一个OutOfMemory错误:

  • spark.driver.memory < 4Gcollect()FAIL 1
  • 4G <= spark.driver.memory < 10Gcount()步(FAIL 2

FAIL 1案例(相关部分)的堆栈跟踪:

[...]
py4j.protocol.Py4JJavaError: An error occurred while calling o1061.collectToPython.
: java.lang.OutOfMemoryError: Java heap space
[...]

执行程序UI不反映过多的内存使用情况(显示使用的&lt; 50k) 驱动程序的内存和执行程序的&lt; 1G)。 Spark指标系统 (app-XXX.driver.BlockManager.memory.memUsed_MB)也没有:它显示 600M到1200M的已用内存,但总是> 300M的剩余内存。 (这表明2G驱动程序内存应该这样做,但事实并非如此。)

首先处理哪个列也没关系(因为它是一个循环 一个dict(),它可以按任意顺序排列。)

我的问题是:

  • 导致OutOfMemory错误的原因以及为什么不是所有可用的CPU核心 用到最后?
  • 当我从执行程序只将几KB转移到驱动程序时,为什么需要10G spark.driver.memory

一些(一般)问题,以确保我理解正确的事情:

  • 如果我收到OOM错误,正确的位置几乎总是驱动程序 (b / c执行程序溢出到磁盘)?
  • 为什么count()会导致OOM错误 - 我认为此操作只会 消耗exector(s)上的资源(向驱动程序提供几个字节)?
  • 上面提到的内存指标(指标系统,用户界面)是否正确 看的地方?

BTW:我在独立模式下运行Spark 2.1.0。

更新2017-04-28

为了进一步向下钻取,我为驱动程序启用了堆转储:

cfg = SparkConfig()
cfg.set('spark.driver.extraJavaOptions', '-XX:+HeapDumpOnOutOfMemoryError')

我使用8G的{​​{1}}运行它,然后分析了堆转储 Eclipse MAT。事实证明,有两个相当大的类(每个大约4G):

spark.driver.memory

我尝试使用

关闭用户界面
java.lang.Thread
    - char (2G)
    - scala.collection.IndexedSeqLike
        - scala.collection.mutable.WrappedArray (1G)
    - java.lang.String (1G)

org.apache.spark.sql.execution.ui.SQLListener
    - org.apache.spark.sql.execution.ui.SQLExecutionUIData 
      (various of up to 1G in size)
        - java.lang.String
    - ...

这使得UI不可用,但对OOM错误没有帮助。另外,我试过了 使用

让UI保持较少的历史记录
cfg.set('spark.ui.enabled', 'false')

这也没有帮助。

更新2017-05-18

我发现了Spark的cfg.set('spark.ui.retainedJobs', '1') cfg.set('spark.ui.retainedStages', '1') cfg.set('spark.ui.retainedTasks', '1') cfg.set('spark.sql.ui.retainedExecutions', '1') cfg.set('spark.ui.retainedDeadExecutors', '1') 方法。 This is like persist but gets rid of the dataframe's lineage。因此,它有助于规避上述问题。

0 个答案:

没有答案