记一次Spark内存溢出与性能优化

场景

执行一个spark任务后,发现控制台一直报错如下:

在这里插入图片描述

21/04/21 10:32:29 ERROR cluster.YarnScheduler: Lost executor 3 on cdh-slave1.test.com: Container killed by YARN for exceeding memory limits.  5.5 GB of 5.5 GB physical memory used. Consider boosting spark.yarn.executor.memoryOverhead or disabling yarn.nodemanager.vmem-check-enabled because of YARN-4714.
21/04/21 10:32:29 ERROR client.TransportResponseHandler: Still have 1 requests outstanding when connection from /172.16.4.156:51552 is closed
21/04/21 10:32:31 ERROR cluster.YarnScheduler: Lost executor 2 on cdh-slave3.test.com: Container killed by YARN for exceeding memory limits.  5.5 GB of 5.5 GB physical memory used. Consider boosting spark.yarn.executor.memoryOverhead or disabling yarn.nodemanager.vmem-check-enabled because of YARN-4714.

执行脚本如下:
在这里插入图片描述

第一反应内存不够了,于是加executor内存:

--executor-memory 6g 

重新执行后报错:
在这里插入图片描述

报错不一样,这次是单个executor因为超过yarn容器限制导致创建失败

查看yarn配置:
在这里插入图片描述

单个容器内存最大6G,总所周知:spark任务单个executor所需内存计算规则:

executorMemory + MAX(executorMemory * 0.10, 384m)

所以单个executor需要6144M+614M的内存,所以单个executor最大只能配置5G内存。

由于是测试环境,内存有限,yarn总共最大24G资源,且还有其他小伙伴在用集群,硬件限制的死死的。

于是先去找领导申请资源,得到的回复是先做优化,正式环境到时候可以适当的加内存。

中国有句古话说的好:在绝对的大内存面前,任何的代码优化都显得花里胡哨。

那只有整点花里胡哨的了,在有限的资源上做优化,目标12G到16G内存之间能支棱起来,时间也不能慢。


又回到原点,修改executor内存5G,还是报最原始的错误。

于是发现事情并不简单。需要仔细排查一下了。

报错还很贴心的提示了让我扩大spark.yarn.executor.memoryOverhead或者禁用yarn虚拟内存检测。

扩大spark.yarn.executor.memoryOverhead

在这里插入图片描述

解决问题,这次正常


性能优化

数据:输入2500W的ADB数据,输出1500WADB数据

硬件:12核,20G内存

当前程序跑完时间:2h

领导说时间太长了,要优化

那就需要研读代码了(我也只是个接盘侠)

查看Spark UI

在这里插入图片描述

发现最后这个Stage切分成了40000多个task

点进去这个Stage查看task:

在这里插入图片描述

居然很多task都是处于空跑的状态

排查代码,一个个的debug查看各个DataFrame的partition

最终定位到其中一个DataFrame数据量很少,但是被分了200个分区,导致再和其他DataFrame做JOIN的时候会扩大成更多的分区,但很多分区内数据都是空的。

解决办法:缩小当前DataFrame的分区数

继续定位其他可优化点:

在这里插入图片描述

发现很多stage中只有一个分区,点进去其中一个看DAG中分区情况

在这里插入图片描述

发现读取jdbc数据就被分为默认1个分区

结合executors页面查看

在这里插入图片描述

妥妥的数据倾斜了

解决方法:修改jdbc读取ADB的分区数

    val modelDataSql = "select pay_time, ...   "
    
    //根据时间分区
    val partitionClause = Array(
                            "EXTRACT(month FROM pay_time) = 1 "
                          , "EXTRACT(month FROM pay_time) = 2 "
                          , "EXTRACT(month FROM pay_time) = 3 "
                          , "EXTRACT(month FROM pay_time) = 4 "
                          , "EXTRACT(month FROM pay_time) = 5 "
                          , "EXTRACT(month FROM pay_time) = 6 "
                          , "EXTRACT(month FROM pay_time) = 7 "
                          , "EXTRACT(month FROM pay_time) = 8 "
                          , "EXTRACT(month FROM pay_time) = 9 "
                          , "EXTRACT(month FROM pay_time) = 10 "
                          , "EXTRACT(month FROM pay_time) = 11 "
                          , "EXTRACT(month FROM pay_time) = 12 "
            )

    sparkSession.read.jdbc(adbProperties.getProperty("url"), s" ( $modelDataSql ) T", partitionClause, adbProperties)



优化后:

在这里插入图片描述

执行时间:10min

破案

前前后后四处定位问题,花了差不多三四天时间,终于解决了

总结:spark分区数,该大时就要大,该小时就要小

参考链接:https://blog.csdn.net/pearl8899/article/details/80368018

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值