背景
大表2T,小表 30G+,两表join到一新表分区。
要求: 尽可能的少用executor core和memory,并减少时间占用
![](https://i-blog.csdnimg.cn/blog_migrate/1615ec7bda954751417aa289bf20a6ca.png)
前提
executor core和memory占用已经够高(--num-executors 200 --executor-cores 4 --executor-memory 30G ),不能再添加过多了。
executor内存 集群统一配置的上限是:yarn.scheduler.maximum-allocation-mb = 30G,
所以 针对spark常见的优化——“把小变量广播出去”变的不太可行。广播的内容会存在每一个executor内,那内存都被广播占满了。
已经做过的优化:
“尽量避免频繁new新的rdd对象”
“用reduceBy代替groupBy 的shuffle算子”
在处理过程中,频繁遇到了oom报错的问题
现象
![](https://i-blog.csdnimg.cn/blog_migrate/91aa34b4ed04890ac825e6520234809e.png)
显然GC时间过久,这还是在使用了reduceByKey的情况下
executors内报错
![](https://i-blog.csdnimg.cn/blog_migrate/0f2e92c439842f4741fad3b66932bbe9.png)
![](https://i-blog.csdnimg.cn/blog_migrate/28b5fcd8f3a6e0cf7ec097b242502b12.png)
CoarseGrainedExecutorBackend: RECEIVED SIGNAL TERM 可以通过配置关闭动态分配
--conf spark.dynamicAllocation.enabled=false
日志里原因: jvm 堆OOM 了
![](https://i-blog.csdnimg.cn/blog_migrate/3ebcaaa1e4e4c828a39ab5ed527e3dc0.png)
解决方案
1 调低为spark缓存保留的内存占比 和 executor里的核数
广义上讲,spark Executor JVM内存可以分为两部分。Spark memory 和 User memory。这由属性spark.memory.fraction 控制 ,他的值介于 0 和 1 之间, 默认为0.6。
在 spark 应用程序中处理图像或进行内存密集型处理时,请考虑减少spark.memory.fraction. 这将为您的应用程序工作提供更多内存。Spark 可以溢出,所以它仍然可以使用较少的内存共享。
问题的第二部分是work的划分。如果可能,将数据分成更小的块。较小的数据可能需要较少的内存。但如果那是不可能的,那么你就是在牺牲计算来换取内存。通常,单个执行程序将运行多个内核。执行器的总内存必须足以处理所有并发任务的内存需求。如果增加执行程序内存不是一个选项,您可以减少每个执行程序的核心数,以便每个任务获得更多内存来使用。使用 1 个核心执行程序进行测试,这些执行程序具有您可以提供的最大可能内存,然后不断增加核心,直到找到最佳核心数
(--num-executors 200 --executor-cores 4 --executor-memory 30G )
2 调整 driver和executor的 spark.yarn.executor.memoryOverhead,提高稳定性
在 YARN,K8S 部署模式下,container 会预留一部分内存,形式是堆外,用来保证稳定性,主要存储nio buffer,函数栈等一些开销。
这部分内存,你不用管堆外还是堆内,开发者用不到,spark也用不到,
所以不用关心,千万不指望调这个参数去提升性能,它的目的是保持运行时的稳定性。
3 添加堆外内存: spark.memory.offHeap.size
作为一个 JVM 进程,Executor 的内存管理建立在 JVM 的内存管理之上,Spark 对 JVM 的堆内(On-heap)空间进行了更为详细的分配,以充分利用内存。同时,Spark 引入了堆外(Off-heap)内存,使之可以直接在工作节点的系统内存中开辟空间,进一步优化了内存的使用。
在默认情况下堆外内存并不启用,可通过配置 spark.memory.offHeap.enabled 参数启用,并由 spark.memory.offHeap.size 参数设定堆外空间的大小。
4 调整driver 的 Xmx 堆 和 Xms 堆
调大driver的内存(Xmx)
修改集群的配置文件 spark-defaults.conf文件
spark.driver.memory 30g
或者直接在spark-submit命令里修改
--driver-memory 30G
这个值默认是1G,即使改大了也不会生效。 SPARK_DRIVER_MEMORY 只设置了 Xmx heap(堆)。初始堆大小保持为 1G,并且堆的大小永远不会扩展到 Xmx 堆。
修改Xms
你有没有转储你的master gc日志?所以我遇到了类似的问题,我发现 SPARK_DRIVER_MEMORY 只设置了 Xmx heap(堆)。初始堆大小保持为 1G,并且堆的大小永远不会扩展到 Xmx 堆。
使用如下配置就看生效了
--conf spark.driver.extraJavaOptions=-Xms10g
ps aux | grep java 你将会看到如下的日志:=
24501 30.7 1.7 41782944 2318184 pts/0 Sl+ 18:49 0:33 /usr/java/latest/bin/java -cp /opt/spark/conf/:/opt/spark/jars/* -Xmx30g -Xms10g
添加配置如下:
--conf spark.dynamicAllocation.enabled=false --conf spark.core.connection.ack.wait.timeout=300
--conf spark.storage.memoryFraction=0.5 --conf spark.shuffle.memoryFraction=0.3
--conf spark.yarn.driver.memoryOverhead=3072 --conf spark.executor.memoryOverhead=3072
--conf spark.memory.offHeap.enabled=true --conf spark.memory.offHeap.size=8g
--conf spark.driver.extraJavaOptions=-Xms20g
参考
https://spark.apache.org/docs/latest/cluster-overview.html
https://www.cnblogs.com/frankdeng/p/9301783.html
https://stackoverflow.com/questions/21138751/spark-java-lang-outofmemoryerror-java-heap-space