订阅更新

Spark Spill 是什么?为什么会频繁发生?

面试问 215 约 3 分钟读完

本文目录
  1. 面试怎么回答
  2. Spill 保护任务但会放大成本
  3. Memory Spill 与 Disk Spill 不等价
  4. 先找是不是少数 Task 特别大
  5. 算子写法会决定工作集大小
  6. 缓存也会和执行内存竞争
  7. 优化后要看整条资源链
  8. 参考资料

面试怎么回答

Spark Spill 是排序、聚合或 Shuffle 的内存数据结构放不下时,把中间数据写入本地磁盘并在后续合并的保护机制。排查时我先看 Spill 集中在哪个 Stage 和算子,再比较 Task 的输入、Shuffle、耗时和 Spill 分布:少数长尾通常是倾斜,普遍偏高则看分区大小、聚合状态、缓存和并发。优先做过滤、预聚合、算子改写和分区治理,最后再评估 Executor 内存与并发。

先把这段讲顺;面试官追问时,再展开下面的原理和细节。

详细讲解

Spark UI 里某个 Stage 的 Spill(Disk) 高达数百 GB,很多人的第一反应是“Executor 内存太小,直接加内存”。可内存翻倍后,任务仍然在疯狂写盘。

Spill 是内存中的排序、聚合或 Shuffle 数据结构放不下时,把中间数据落到本地磁盘的保护机制;它是结果,不是单一根因。

Spark Spill 常由单 Task 数据量过大、倾斜、聚合状态膨胀或内存竞争触发;应先定位算子和异常分区,再调整数据路径,最后才是内存参数。

Spill 保护任务但会放大成本

排序、聚合和 Shuffle 会在执行内存中维护缓冲区、哈希表或排序器。申请不到足够内存时,Spark 把部分数据序列化写到本地盘,之后再合并。这样能避免立刻 OOM,但增加序列化、磁盘 I/O、合并和 GC。

Memory Spill 与 Disk Spill 不等价

UI 中 Memory Bytes Spilled 常表示溢写数据在反序列化内存形态下的估算大小,Disk Bytes Spilled 是压缩或序列化后实际写盘量。不能用二者差值判断“内存丢了多少”,要结合记录数、压缩和算子理解。

Spill 是怎样发生的

先找是不是少数 Task 特别大

查看 Stage 的 Task 分布:输入、Shuffle Read、运行时间和 Spill 是否长尾。如果只有一个或少数 Task 异常,优先怀疑热点 Key 或不均匀分区;如果全部 Task 都高,可能是分区普遍过大、宽聚合或内存配置不合理。

算子写法会决定工作集大小

groupByKey 会保留更多明细,reduceByKey 或预聚合可以缩小中间数据;collect_list、高基数去重和宽行排序会扩大单 Task 状态。Join 前过滤、列裁剪、局部聚合和合理分区,往往比单纯加堆更有效。

Spill 排查:先分布,后参数

缓存也会和执行内存竞争

Spark 的执行与存储共享统一内存区域,执行可以逐步驱逐缓存,但用户对象、元数据和堆外开销仍占空间。缓存过多、对象膨胀或同一 Executor 并发 Task 太多,都可能让每个 Task 获得的工作内存变少。

优化后要看整条资源链

调整分区数、处理倾斜或改写算子后,我会对比 Spill、峰值磁盘、GC、Shuffle Read/Write 和总耗时。Spill 降低不代表一定更快:分区过细会增加调度和小文件,目标是让任务在资源与吞吐之间稳定。

参考资料

刷题进度保存在当前浏览器