Spark配置参数详解:spark-defaults.conf核心参数、动态资源分配、序列化与内存模型配置
说实话,很多Spark跑得慢,不是代码写得烂,是配置压根没调过。我见过太多团队,集群搭好以后就扔在那,默认配置一用到底。结果呢?数据量一上来,OOM、GC频繁、任务卡死,全来了。
这一章,咱们就把配置这件事彻底讲透。我会从spark-defaults.conf的核心参数说起,再到动态资源分配、序列化选型,最后把内存模型掰开揉碎。你跟着走一遍,至少能解决80%的配置问题。
spark-defaults.conf:你的第一道防线
这个文件是Spark启动时自动加载的默认配置。说白了,你在这里写好的参数,每个任务都会继承。我个人习惯是把它放在$SPARK_HOME/conf/目录下,集群里所有节点保持一致。
先看几个最关键的参数:
| 参数名 | 默认值 | 我的建议值 | 说明 |
|---|---|---|---|
spark.executor.memory |
1g | 4g-8g | 每个Executor的堆内存,别给太少,也别超过节点物理内存的75% |
spark.executor.cores |
1 | 2-4 | 每个Executor的CPU核数,太多会导致HDFS I/O瓶颈 |
spark.driver.memory |
1g | 2g-8g | Driver内存,collect操作多的话一定要加 |
spark.sql.shuffle.partitions |
200 | 根据数据量动态设 | Shuffle后的分区数,200是通用值,大表建议500-1000 |
spark.default.parallelism |
取决于集群 | Executor数×cores×2~3 | RDD操作的默认并行度,设太少了任务跑不满 |
动态资源分配:别让资源闲着
默认情况下,Spark会一次性申请你指定的所有资源。但很多时候,任务有波峰波谷。比如凌晨跑批的时候负载高,白天查询少。这时候动态资源分配就派上用场了。
开启方式很简单:
spark.dynamicAllocation.enabled true
spark.dynamicAllocation.minExecutors 1
spark.dynamicAllocation.maxExecutors 50
spark.dynamicAllocation.initialExecutors 5
spark.shuffle.service.enabled true
这里有个坑——spark.shuffle.service.enabled必须设为true。为什么?因为Executor动态增减时,Shuffle数据需要有个外部服务来管理,不然数据就丢了。我记得有一次没开这个,结果Executor缩容后,后续Stage死活找不到Shuffle输出,排查了半天。
序列化:选对方式,性能翻倍
Spark里数据要在网络间传输,要落盘,要缓存,这些都离不开序列化。默认用的是Java序列化,但说实话,它又慢又占空间。
我强烈建议换成Kryo序列化:
spark.serializer org.apache.spark.serializer.KryoSerializer
spark.kryo.registrationRequired true
spark.kryo.referenceTracking false
spark.kryoserializer.buffer.max 256m
你可能会问:为什么要注册类?因为Kryo默认不记录类名,只记录ID,这样更省空间。但如果你不注册,它就得用全类名字符串,效果大打折扣。
注册方式也很简单:
// 在Driver端注册
val conf = new SparkConf()
.registerKryoClasses(Array(
classOf[MyClass1],
classOf[MyClass2]
))
内存模型:别再让OOM追着你跑
Spark的内存管理,很多人觉得复杂。其实说白了,就两大块:执行内存和存储内存。执行内存用来做Shuffle、Join、Aggregation这些计算操作;存储内存用来缓存RDD、DataFrame。
默认情况下,Spark把堆内存的60%分给这两块,剩下的40%留给用户代码和元数据。这60%里,执行和存储各占一半,但可以互相借用。
我画了一张图,帮你理清这个结构:
这里有个关键点:执行内存可以抢占存储内存。如果你的任务里Cache了很多数据,但突然来了一个大的Shuffle操作,执行内存不够了,它会从存储内存里抢。抢走的块会被驱逐(Evict),如果被驱逐的块还没计算完,就得重新算。
spark.memory.storageFraction从0.5调到了0.7,给存储内存留了更多空间,问题就解决了。
几个实战调优建议
说了这么多,我总结几条立竿见影的配置:
- Executor内存别贪大:4-8g最合适,超过8g考虑多加Executor
- Kryo一定要开:注册好类,性能提升肉眼可见
- 动态资源分配配External Shuffle Service:缺一不可
- Shuffle分区数别死用200:大表建议用
spark.sql.adaptive.coalescePartitions.enabled=true让Spark自动调整 - 内存比例根据场景调:Cache多就提高storageFraction,计算多就降低