【Spark性能调优】Spark Application启动资源规格调优

背景

无论是Spark on Yarn还是Spark on k8s场景。当我们有了一个资源队列(资源池)时,如何划分资源能够让任务执行的更高效,是我们每个人都会遇到的问题。

Spark Application的组成,及各自角色

Spark Application是由1个driver和多个Executor来组成的。Driver 负责协调 Spark 作业的执行,调度任务,并且负责将作业分解为多个 Stage 和 Task。它还负责维护整个应用程序的状态。 Executor 是实际执行任务的工作节点。每个 Executor 可以并行处理多个任务,并且在任务执行过程中处理数据的计算。

根据driver和Executor在计算任务时所扮演的角色。我们就更能知道资源调整方向。

Spark资源相关参数

基本参数

参数名含义默认值
spark.executor.instancesexecutor的个数2
spark.executor.cores单个executor核数1
spark.executor.memory单个executor的内存1G
spark.driver.coresdriver的核数1
spark.driver.memorydriver的内存1G

高级参数

参数名含义默认值
spark.executor.memoryOverheadexecutor堆外内存384
spark.driver.memoryOverheaddriver堆外内存384
spark.memory.fraction该参数设置了Spark应用程序中可用于执行和存储数据的堆内存比例。默认值为0.6,即60%的堆内存将被分配给Spark的执行和存储数据结构。0.6
spark.memory.storageFraction该参数用于定义在Spark的总内存中,专门用于存储数据的内存比例。具体来说,这个参数控制了可用于缓存RDD、DataFrame以及其他数据结构的内存量。0.5
spark.driver.memoryOverheadFactor该参数设置了dirver堆外内存和堆内内存的比例,如果设置了spark.driver.memoryOverhead参数则本参数不生效0.10
spark.executor.memoryOverheadFactor该参数设置了executor堆外内存和堆内内存的比例,如果设置了spark.executo.memoryOverhead参数则本参数不生效0.10

PS. 以上参数详见官方文档:
https://spark.apache.org/docs/latest/configuration.html

Spark资源调优策略

确定Driver

一般来说,由于driver在sc中的分工,所以不需要很大核数,一般1核就够。内存一般默认1G就够用,只有在进行collect,toPandas等需要将executor数据回写到driver才会容易导致oom,需要调大driver内存,或者优化代码逻辑。所以这里给driver分配1个cpu,1GB内存,堆外内存默认384MB一般不需要修改。

确定Executor

核数和内存的比例

剩下的资源都留给executor,Spark官方给出的内存cpu比例是2:1-4:1,也就是每个cpu对应2-4G内存。这个比例确定后,我们通过cpu就可以得到对应的内存。

使用多个 Executors 但每个 Executor 的核心数较小,还是使用较少的 Executors 但每个 Executor 的核心数较大?

同样的cpu我们可以少executor,每个executor多cpu。或者多executor每个executor少cpu。举个例子来说,有10个cpu,我们可以起10个executor,每个executor 1核。也可以1个executor,每个executor10核。当然对应的内存是按照比例分配的,也会影响单个Executor内存大小。

所以区别主要在以下三个点:executor个数,单个executor核心/内存

我们可以从以下几个方面来分析:

任务的性质:计算密集型 vs I/O 密集型
计算密集型任务

如果任务主要是计算(例如复杂的数学运算或数据转换),则使用较少的 Executors 但每个 Executor 有更多的核心可能会更高效。这是因为任务可以更好地利用 CPU 计算资源。

I/O 密集型任务

如果任务涉及大量的 I/O 操作(包括磁盘I/O:读取和写入数据;网络I/O:shuffle),使用更多的 Executors 可能更有利。更多的 Executors 可以并行处理 I/O 操作,从而减少整体的等待时间。

如何判定是计算密集型还是I/O密集型?

可以根据Spark宽窄依赖,如果执行的任务(job)窄依赖比较多,可以说明不需要跨executor操作,executor内部用本地数据交换即可完成,也就是计算密集型任务,所以单个Executor多核心数会比较合适。如果宽依赖比较多,说明经常需要executor回传数据到driver,也就是I/O密集型任务,所以多个Executor,单个Executor核心数少比较合适。

拓展:如何判断宽窄依赖?
  • 可以通过编写代码时的算子,通过行动算子和转换算子来判断。
  • 如果不熟悉代码编写逻辑,或者是直接执行的sql,由spark来进行转换操作,那可以看SparkUI,进入stage,查看DAG图,都执行了哪些操作,进行进一步分析。
任务的并行度

Spark 的并行度是由任务的数量和可用的 Executors 以及每个 Executor 的核心数共同决定的。增加 Executors 的数量可以提高任务的并行度,从而缩短整体处理时间。
设置合理的并行度:如果任务的并行度较高,使用多个 Executors(即使每个核心数较小)通常会更好,因为这样可以充分利用集群的资源。可以通过设置 spark.default.parallelism 来调整并行度。

资源利用率

使用多个 Executors 可能会提高资源利用率,尤其是在集群中有其他作业并发运行时。如果 Executors 数量较少,可能会导致部分核心闲置,从而影响整体资源利用效率。

任务调度

更多的 Executors 可以减少调度延迟。在 Spark 中,每个 Task 是通过 Executors 执行的,多个 Executors 可以同时处理多个 Task,从而提高任务调度的效率。

内存管理

内存使用:每个 Executor 使用的内存量也会影响性能。如果您选择使用多个 Executors,每个 Executor 的内存可能会较少,这可能会导致中间数据的频繁溢出到磁盘,降低性能。
内存开销:每个 Executor 都会有一些内存开销(如 JVM 的管理开销),多个 Executors 会增加总体内存开销,而少数 Executors 则可以更好地共享内存资源。

总结
  • 选择多个 Executors(核心数小):如果任务是 I/O 密集型,或者需要更高的并行度、资源利用率和较低的调度延迟,选择多个 Executors 更有利。
  • 选择少数 Executors(核心数大):如果任务是计算密集型,且能够合理利用更多的计算资源,选择少数 Executors 但每个 Executor 的核心数较大可能更有效。

以上只是理论上的分析和调优方向,实际生产中,通过理论得到第一版方案,进行试验,通过监控找到瓶颈,分析瓶颈并进一步试验。是一个迭代的过程,通过试验和监控来评估不同配置下的性能表现,从而优化资源配置。毕竟实践是检验真理的唯一标准。

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值