You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spark为何发生磁盘溢写?大资源配置下的溢写疑问

Spark Shuffle全量磁盘溢写原因分析

环境配置梳理

  • 集群资源:18个Executor,单Executor配置102GB内存、26核,集群总内存1836GB、总核数468
  • Spark内存参数:
    --conf "spark.memory.fraction=0.6"
    --conf "spark.memory.storageFraction=0.1"
    
  • 内存配额计算:单Executor的Spark统一内存池为102GB * 0.6 = 61.2GB,其中执行内存约61.2GB * (1-0.1) = 55.08GB,集群总执行内存约18 * 55.08GB ≈ 991GB(注:原计算存在偏差,此处按参数逻辑修正)
  • 作业特征:大型Join触发全量Shuffle,Shuffle写入压缩后534.9GB、未压缩3.9TB,使用Kryo序列化,无缓存/广播操作

全量溢写的核心原因

1. Shuffle写的内存分配逻辑限制

Spark的执行内存(Execution Memory)主要用于Shuffle读阶段的聚合、Join计算,并非用于Shuffle写阶段的输出缓存。Shuffle写依赖的是独立的小缓冲区:

  • 默认spark.shuffle.file.buffer=32KB,每个ShuffleWriter仅持有该大小的缓冲区,数据满额后直接刷盘,缓冲区总量极小,无法承载GB级Shuffle数据。
  • Shuffle写的核心逻辑是按分区排序后刷盘,设计上就不会将大量Shuffle输出长期驻留内存。

2. 单Task的内存配额不足

集群总内存充足,但单个Task的可分配内存有限:

  • 每个Executor运行26个并行Task(与核数一致),单Task可分配的执行内存约55.08GB / 26 ≈ 2.1GB。
  • 单Task需处理的未压缩数据约3.9TB / 468 ≈ 8.3GB,压缩后约1.3GB,接近甚至超过单Task内存配额,触发内存溢出并全量刷盘。

3. SortShuffleManager的默认行为

Spark 3.x默认使用SortShuffleManager,其流程决定了Shuffle写必然刷盘:

  • 若Shuffle分区数超过spark.shuffle.sort.bypassMergeThreshold=200,Task会将数据写入内存排序缓冲区,满额后溢写磁盘,最终合并临时文件。
  • 若分区数少于200,启用Bypass模式,Task直接写入临时文件后合并,同样不会将数据保留在内存中。

4. 实际可用内存的损耗

理论内存配额需扣除JVM开销:

  • Executor的JVM元空间、GC预留内存等会占用约40%的堆内存(spark.memory.fraction=0.6意味着仅60%堆内存给Spark统一池),实际可用于计算的内存比理论值更低。

验证与优化方向

  • 查看Spark UI的Executors页,确认统一内存池的实际占用,验证内存配额是否符合预期。
  • 调整Shuffle参数:
    • 增大spark.shuffle.file.buffer(如设为64KB/128KB),减少刷盘次数,但无法避免溢写。
    • 若Shuffle分区数较少,调高spark.shuffle.sort.bypassMergeThreshold启用Bypass模式,降低排序开销。
  • 优化Shuffle分区数:调高spark.sql.shuffle.partitions,减少单Task处理的数据量,降低内存压力。
  • 排查数据倾斜:通过Spark UI的Tasks页查看单Task的Shuffle写大小,若存在极个别Task写入量远超均值,需针对倾斜Key做拆分、加盐处理。

内容的提问来源于stack exchange,提问作者Klun

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 02:52:14