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 拉到 DrivertoLocalIterator()在 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
推荐排查流程
- 查看 Spark UI 的 Executors 标签页,确认是哪个 Executor 出现 OOM
- 查看该 Executor 的 Storage 和 Shuffle 指标,定位内存消耗来源
- 使用 SizeEstimator 量化关键数据结构的大小
- 根据 OOM 类型选择对应的调优方案
- 调参后在测试环境验证,避免一次性修改多个参数


