AWS EMR Spark任务写入Parquet时遇Java堆内存溢出求助
问题描述
我有一个AWS EMR Spark任务需要处理1.3TB数据,HDFS默认3副本。当前使用20台m5.24xlarge核心节点,每台配置768GB EBS卷,任务运行约1.5小时后抛出OOM错误:
# # java.lang.OutOfMemoryError: Java heap space # -XX:OnOutOfMemoryError="kill -9 %p" # Executing /bin/sh -c "kill -9 27571"...
尝试过调整HADOOP_HEAPSIZE从2048到6144,也测试过多种实例类型、数量、EBS卷及堆内存配置组合,均未解决。当前Spark核心配置:
sparkConf.set("spark.driver.memory", "10g"); sparkConf.set("spark.executor.memory", "16g");
仅在执行spark.write.parquet(任务最后一步)时失败,求解决建议。
解决建议
1. 优化Spark写阶段的内存与分区配置
- 调整
spark.sql.shuffle.partitions:默认200的分区数对于1.3TB数据来说太少,会导致单分区数据量过大,Executor内存过载。建议按节点数 * 每节点Executor数 * 每Executor核心数设置,比如m5.24xlarge单节点24vCPU,按每Executor分配8核计算,单节点可开3个Executor,20节点则设置为spark.sql.shuffle.partitions=480,甚至可上调至1000,确保单分区数据量控制在1GB以内。 - 开启自适应执行计划:设置
spark.sql.adaptive.enabled=true,搭配spark.sql.adaptive.shuffle.targetPostShuffleInputSize=256m和spark.sql.adaptive.advisoryPartitionSizeInBytes=256m,让Spark根据运行时数据量自动调整分区大小,避免写阶段出现超大分区。 - 合理分配节点内存:m5.24xlarge单节点有96GB内存,当前仅给Executor分配16GB严重浪费资源。建议预留10GB给系统,每Executor分配20GB,单节点可开4个Executor(4*20=80GB),同时设置
spark.executor.memoryOverhead=4g(Executor内存的20%),避免堆外内存不足触发OOM。
2. 优化Parquet写入参数
- 启用矢量化写入:设置
spark.sql.parquet.enableVectorizedWriter=true,大幅降低写入时的内存占用,提升写入效率。 - 选择合适的压缩编码:设置
spark.sql.parquet.compression.codec=snappy,在压缩比和写入速度间取得平衡,减少写入阶段的数据量。 - 关闭不必要的Schema合并:如果任务不需要合并Schema,设置
spark.sql.parquet.mergeSchema=false,避免额外的内存开销。
3. 排查存储与HDFS问题
- 提升EBS卷IO性能:若使用gp2卷,高负载下可能出现IOPS瓶颈,导致数据堆积在内存中。可更换为gp3卷并指定10000+ IOPS,或改用m5d.24xlarge的本地NVMe存储,提升写入吞吐量,避免内存积压。
- 检查HDFS可用空间:执行
hdfs dfsadmin -report确认集群可用空间,排除因其他任务占用、块损坏导致的空间异常,确保写入时有足够存储缓冲。
4. 精准定位OOM来源
- 区分Driver/Executor OOM:查看YARN日志,确认是Driver还是Executor进程被kill。若为Driver OOM,当前10GB内存可能不足以支撑写阶段的元数据收集,可调整
spark.driver.memory=20g并搭配spark.driver.memoryOverhead=4g。 - 生成堆转储分析:设置
spark.executor.extraJavaOptions="-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof",通过堆转储文件分析内存占用的对象类型,明确是缓存数据、Shuffle数据还是写入缓冲区导致的OOM。
内容的提问来源于stack exchange,提问作者seou1
相关产品推荐
相关产品推荐

