Spark 任务 OOM 排查与内存参数调优

用 SizeEstimator 和 Web UI 量化内存消耗,给出 Executor Driver 与数据倾斜三类 OOM 的诊断模板

袋鼠云
袋鼠云
经营分析

Spark OOM 是离线作业最常见的故障之一,但很多团队的排查方式是"凭经验调大内存"。真正的排查应该从量化内存消耗开始,区分 Executor OOM、Driver OOM 和数据倾斜三类场景,再针对性调参。

第一步:量化内存消耗

使用 SizeEstimator 工具:

import org.apache.spark.util.SizeEstimator
val rdd = sc.parallelize(largeDataset)
println(s"Estimated size: ${SizeEstimator.estimate(rdd)} bytes")

SizeEstimator 可以精确估算 RDD、DataFrame 或任意 Java 对象在内存中的实际占用。这比看 Web UI 的 Storage 页面更准确,因为 Storage 页面只显示序列化后的大小。

Web UI Storage 页面分析:

打开 Spark Web UI 的 Storage 标签页,关注以下指标:

  • Cached Partitions:如果 Cached 数据量接近 spark.memory.storage 上限,说明存储内存不足
  • Unroll Memory:如果 Unroll Memory 持续增长,说明有大量数据需要反序列化
  • Shuffle Write/Read:Shuffle 数据量过大是 Executor OOM 的常见诱因

第二步:区分 Executor OOM 与 Driver OOM

Executor OOM 的典型症状:

日志中出现 java.lang.OutOfMemoryError: Java heap space,堆栈指向 Shuffle、Sort 或 aggregation 操作。

根因:Executor 的执行内存不足以处理当前 Task 的数据量。常见于:

  • Shuffle 分区数过少,导致单个 Task 处理的数据量过大
  • collect() 或 toLocalIterator() 将大量数据拉到 Executor 内存
  • UDF 中缓存了过大的对象

Driver OOM 的典型症状:

日志中出现 java.lang.OutOfMemoryError: Java heap space,堆栈指向 DAGScheduler、TaskScheduler 或 collect() 操作。

根因:Driver 需要收集所有 Executor 的结果。常见于:

  • collect() 将整个 DataFrame 拉到 Driver
  • toLocalIterator() 在 Driver 端累积数据
  • Broadcast 变量过大,超过 spark.driver.maxResultSize

离线开发 BatchWorks

支持多模态、覆盖集成开发调度运维的一站式离线数据开发平台

了解产品

第三步:Executor OOM 调优

内存参数速查表:

spark.executor.memory: 8g           # Executor 总堆内存
spark.memory.fraction: 0.6          # 统一内存占堆内存比例(默认 0.6)
spark.memory.storageFraction: 0.5   # 存储内存占统一内存比例(默认 0.5)

统一内存 = 堆内存 × memory.fraction,其中存储内存 = 统一内存 × memory.storageFraction,执行内存 = 统一内存 - 存储内存。

关键调优原则:

  • 如果作业以 Shuffle/Aggregation 为主(如 GROUP BY、JOIN),增大执行内存:降低 memory.storageFraction 到 0.3-0.4
  • 如果作业以缓存为主(如反复查询同一 DataFrame),增大存储内存:提高 memory.storageFraction 到 0.6-0.7
  • Shuffle 分区数不足时,增大 spark.sql.shuffle.partitions(默认 200),让每个 Task 处理更少数据

数据倾斜 OOM 的特殊处理:

当某个 Task 的处理时间远超其他 Task(如 10 倍以上),说明存在数据倾斜。

处理方案:

  • Salting:对倾斜 Key 添加随机后缀,分散到多个 Task 处理后再聚合
  • Broadcast Join:将小表广播到所有 Executor,避免 Shuffle
  • Adaptive Execution:Spark 3.0+ 的 AQE 可以自动检测倾斜分区并拆分:spark.sql.adaptive.skewJoin.enabled=true

第四步:Parquet/Snappy 读取 OOM

读取 Parquet 文件时出现 OOM 的常见原因是:压缩后的 Block 在内存中解压后体积膨胀。

Snappy 压缩的解压比通常为 3-5 倍。如果 Parquet 文件的 Row Group Size 为 256MB,解压后可能达到 1GB。

解决方案:

  • 减小 Parquet 文件的 parquet.block.size(写入时控制)
  • 增大 Executor 内存或增加 Executor 数量(读取时缓解)
  • 使用 spark.sql.parquet.filterPushdown=true 减少读取的数据量

数据治理

统一标准、质量、安全与资产管理,沉淀高质量数据集,支撑经营决策、监管合规与 AI 应用。

了解解决方案

常见错误配置

  • 过度设置 `spark.memory.offHeap.size`:堆外内存如果设置过大但未启用 spark.memory.offHeap.enabled=true,会导致实际可用内存减少
  • Executor 内存超过 NodeManager 可用内存:导致 Container 被 YARN 强杀,表现为 Container killed by YARN for exceeding memory limits
  • Driver 内存设置过小但 `maxResultSize` 过大:Driver 尝试接收超过自身堆内存的结果,导致 OOM

推荐排查流程

  1. 查看 Spark UI 的 Executors 标签页,确认是哪个 Executor 出现 OOM
  2. 查看该 Executor 的 Storage 和 Shuffle 指标,定位内存消耗来源
  3. 使用 SizeEstimator 量化关键数据结构的大小
  4. 根据 OOM 类型选择对应的调优方案
  5. 调参后在测试环境验证,避免一次性修改多个参数

开启Data+AI 之旅

填写表单,获取专属解决方案

400-002-1024

工作日 9:00-18:00

关注公众号

获取最新产品动态与技术干货

关注公众号

企业微信

扫码添加企业微信