概述
基于论坛上一些关于spark内存设置的文章,我对一个项目中实际运行的任务进行了内存参数分析和优化。如果要了解更多详细设置原理,可见文末的参考文章链接。
已知内存分配存在通过用户提交的参数设置进行静态分配,和yarn进行动态分配两种,所以本文对两种状况都根据实际场景进行了分析。
资源详情
配置spark的内存参数,首先要基于集群资源的情况。本例的机器情况:一共8台机器,即8个Node。核心数:32 core / node;内存资源:50.8GB RAM/ node。
每个Node在计算的时候,给操作系统和Hadoop的进程预留2core,2.8GB,所以每个节点剩下28个core和48GB内存。
任务A——推断动态分配内存的过程
发现任务提交的参数设置为:
executor-memory=8g;num-executors=10;executor-cores=3。考虑到有MemoryOverhead,发现任务有参数将其设置为1G。(默认值是max(384M, 0.07 × spark.executor.memory))
因此按参数配置,本任务占用总内存为(8+1)10=90gb;核心数为310=30。可见一定没有占满,那么系统会基于memory动态分配core和executor-num。
executor个数:num-executors
因为一个executor其实可以跨node运行,因此用总可用内存数除以每个executor的内存数,可得总计num-executors=320/8=40,考虑有可能有1GB的MemoryOverhead,则num-executors应为320/9≈36,所以系统可能会动态分配36~40个executor。
也可能Yarn比我们设想的还要狠,给其他应用预留的空间更少,那么极限情况下把所有可用内存都给这个应用,那么最大的num-executors=(50.88)/8≈50,考虑MemoryOverhead,则num-executors应为(50.88)/9≈45。
最终,可以猜测Yarn动态分配的Executor个数在36~50个之间,40左右较为合理。
通过查看web页面的某个stage的执行计划,可以发现确实是40个executor。
core的个数
本参数决定一个executor能够并发任务的个数。所以通常认为,一个executor越多的并发任务能够得到更好的性能。但有研究显示一个应用并发任务超过5,导致更差的性能。
目前已知共有40个executor,8个node,则一个Node平均5个executor。同时一个Node30个核(去掉预留的2个),那么如果动态分配,可能最多给每个Executor动态分配30/5=6个Core。
从Yarn的总Core分配界面可以看到差不多是这样。当然,如果有更好的方法从WEB界面的Spark ApplicationMaster里查看到core的数量,欢迎分享。
driver-memory大小
driver运行内存,默认值512m,一般2-6G。目前来看该参数对任务运行没有太大的影响,本案例中的设置值为4g。即,driver-memory=4g
————后来实际运行任务时,查看执行日志可以发现,这个任务总在报内存不足的错误,因此考虑设置合理的静态内存分配参数,把executor的内存调大,这就可以参考下面一个任务的设置过程。
修改之后,Yarn上的资源分配情况,可以看出确实和新设置的参数很符合,即1个Executor占20G内存。
任务B——静态分配内存参数的设置过程
因为该任务经常报超内存的错误,所以先设置memory大小。
memory大小
配置每个executor的内存,一个node,48G内存可用。考虑到集群还会有其他任务运行,所以给其他任务留8GB,所以设置每个 executor可配置内存为40GB。这也意味着一个node1个executor。
从Spark的内存模型角度,Executor占用的内存分为两部分:ExecutorMemory和MemoryOverhead,预留出MemoryOverhead的内存量之后,才是ExecutorMemory的内存。
MemoryOverhead的计算公式: max(384M, 0.07 × spark.executor.memory)
因此 MemoryOverhead = 0.07 × 40G = 2.8G=2867MB 约等于3G > 384M
最终executor的内存配置值为 40G – 3 =37 GB
因此设置:executor-memory = 37 GB;spark.executor.memoryOverhead=3*1024=3072
core的个数
决定一个executor能够并发任务的个数。所以通常认为,一个executor越多的并发任务能够得到更好的性能。在本次设置中,理论上来说,由于1个node只有1个executor,所以可以用24个core。但有研究显示一个应用并发任务超过5,导致更差的性能。
所以core的个数暂设置为5个。
5个core是表明executor并发任务的能力,并不是说一个系统有多少个core,即使我们一个CPU有32个core,也设置5个core不变。
因此设置:executor-cores=5
executor个数
因为已经设置好1个node只有1个executor,所以,之后通过每个node的executor个数,可以得到整个任务可以分配的executors个数。
我们有8个节点,每个节点1个executor,8 × 1 = 8个executors,额外预留1个executor给AM,最终要配置7个executors。
因此设置:num-executors=7
driver-memory大小
driver运行内存,默认值512m,一般2-6G。目前来看该参数对任务运行没有太大的影响,因此就设置4g。
因此设置:driver-memory=4g
参考文章
对参数设置讲的很细的一篇,本文重点参考:Spark任务的core,executor,memory资源配置方法
对Spark Executor Memory做系统讲解,特别是讲到内存分为执行内存和存储内存两部分。还介绍了一些别的参数:如何设置Spark Executor Memory的大小
延申的内存调优,即对shuffle过程的参数调优,这个本例没有用到,有兴趣的话可以尝试一下:Spark学习(五)Spark Shuffle及内存分配