Spark OOM问题避免策略与示例说明
一、资源调优
-
增加Executor内存
通过调整Executor内存参数,确保任务有足够资源处理数据:spark-submit --executor-memory 8G --driver-memory 2G ... # 示例命令- 场景示例:处理10TB级日志时,将
executor-memory从4G提升至8G,减少内存溢出风险。
- 场景示例:处理10TB级日志时,将
-
优化内存分配比例
--conf spark.memory.fraction=0.8 # 提高内存利用率
二、数据分区优化
-
合理设置分区数
- 默认分区数过低:导致单分区数据量过大,触发OOM。
val df = spark.read.parquet("path").repartition(200) # 根据集群核数调整 - AQE自动优化(Spark 3.0+):
SET spark.sql.adaptive.enabled=true; # 自动合并小分区
- 默认分区数过低:导致单分区数据量过大,触发OOM。
-
自定义分区器解决数据倾斜
- 场景示例:某用户ID占90%数据量,通过自定义分区器将热点数据分散到多个分区:
class SkewPartitioner(numParts: Int, hotKeys: Set[String]) extends Partitioner {
override def getPartition(key: Any): Int = {
if (hotKeys.contains(key.toString)) (key.hashCode % 10).abs else (key.hashCode % numParts).abs
}
}
val rdd = dataRdd.partitionBy(new SkewPartitioner(100, Set("user_123"))) # 分散热点Key
- 场景示例:某用户ID占90%数据量,通过自定义分区器将热点数据分散到多个分区:
三、代码逻辑优化
-
避免高内存算子
- 使用
reduceByKey替代groupByKey:// groupByKey导致全量数据Shuffle,内存压力大
val badRdd = rdd.groupByKey()
// reduceByKey在Map端预聚合,减少数据量
val goodRdd = rdd.reduceByKey(_ + _) # 推荐方式
- 使用
-
谨慎使用
cache()和persist()- 示例:仅缓存频繁访问的数据,避免内存浪费:
val filtered = df.filter(...).cache() # 仅缓存必要数据
- 示例:仅缓存频繁访问的数据,避免内存浪费:
-
避免大对象转换
- 场景示例:排序时直接操作
Iterable而非转换为List:rdd.mapValues(_.toArray.sorted) # 替代.toList避免OOM
- 场景示例:排序时直接操作
四、数据倾斜处理
-
盐化(Salting)技术
- 场景示例:对热点Key添加随机前缀,分散数据后二次聚合:
val salted = df.withColumn("salted_key", concat($"user_id", lit("_"), floor(rand()*10)))
val agg = salted.groupBy("salted_key").agg(sum("value"))
val result = agg.withColumn("user_id", split($"salted_key", "_")(0)).groupBy("user_id").sum("value") # 分阶段聚合
- 场景示例:对热点Key添加随机前缀,分散数据后二次聚合:
-
广播小表代替Shuffle Join
val smallTable = spark.table("small_table").collect()
val broadcastTable = spark.sparkContext.broadcast(smallTable)
rdd.mapPartitions(iter => { val localTable = broadcastTable.value // 本地Join逻辑 }) # 避免大表Shuffle
五、监控与诊断
-
Spark UI分析
- 关键指标:
- Shuffle Read Size:检查各Task数据量是否均衡。
- GC Time:高GC时间提示内存压力。
- 关键指标:
-
日志排查
- 错误类型:
Java heap space:需增加内存或优化代码。GC overhead limit exceeded:提示内存回收效率低,需减少对象创建。
- 错误类型:
总结
| 策略 | 适用场景 | 示例 |
|---|---|---|
| 资源调优 | 内存不足或Executor配置不合理 | 调整executor-memory和spark.memory.fraction |
| 分区优化 | 数据倾斜或分区不均 | 自定义分区器分散热点Key |
| 代码逻辑优化 | Shuffle数据量大或缓存不当 | 使用reduceByKey替代groupByKey |
| 盐化技术 | 极端数据倾斜(如Top-N热点Key) | 添加随机前缀分阶段聚合 |
注:实际需结合业务场景综合使用多种策略,并通过监控工具验证效果。
本文来自博客园,作者:业余砖家,转载请注明原文链接:https://www.cnblogs.com/yeyuzhuanjia/p/18790880

浙公网安备 33010602011771号