写点什么

Spark 性能调优

  • 2022 年 8 月 12 日
    北京
  • 本文字数:1737 字

    阅读完需:约 6 分钟

作者: Billmay 原文来源:https://tidb.net/blog/5957fd9e

该文档仅提供性能调优的参考

资源调优

Spark 可以通过参数配置资源分配。资源分配不合理会导致 job 运行过慢甚至失败。资源调优就是为当前 job 分配合适的资源,提高资源利用率最终加快任务运行速度。

Driver

Executor

内存


  • 太大的内存会导致 JVM 垃圾回收变慢,尽量小于 64 g

  • executor 申请的总内存不能超过 node/container 的总内存,申请的内存大小为以下参数总和

  • spark.executor.memory

  • spark.executor.memoryOverhead

  • spark.memory.offHeap.size

  • spark.executor.pyspark.memory


CPU


  • 1 core 会导致无法利用 JVM 多线程,以及会使一些 broadcast 相关的参数失效

  • core 设置太多可能会使 job 速度运行变慢


Executor 内存分配

指 spark.executor.memory 的内存分配



Spark 的内存分为两大类:执行内存和存储内存。


  • 执行内存:在 shuffle, join, aggregation 等计算中使用的内存。

  • 存储内存:集群中缓存和 broadcast 使用的内存。


存储内存和执行内存共享同一块空间,且有动态占用机制


  • 双方基础内存占比由 spark.memory.storageFraction 决定

  • 一方空闲时,另一方可以占用

  • 当执行内存不足,且存在被占用内存时:可要求存储内存归还占用部分。存储内存会将占用部分转存到磁盘

  • 当存储内存不足,且存在被占用内存时:不可要求执行内存归还


用于 task 的执行内存大小可以计算得出


spark.executor.memory * spark.memory.fraction *(1-spark.memory.storageFraction)/ spark.executor.cores
复制代码


总结:


  • 发生磁盘溢写时:可尝试调大 spark.executor.memory 或提高 spark.memory.fraction

  • spark.memory.storageFraction 一般取默认值即可,不太推荐在溢写时调小该值

动态内存分配

Spark 提供了 Dynamic Executor Allocation ,它能够动态调整 executor 数量,以下场景可以考虑配置


  • 和其他团队共享集群

  • 在乎 cost

  • 某一个 application 有若干不同大小的 job


主要参数如下


spark.dynamicAllocation.enable false
spark.dynamicAllocation.executorIdleTimeout 60s // 如果任务执行时间普遍短,可以调小 timeout
spark.dynamicAllocation.initialExecutors minExecutors // 对于大的 job,调大 initialExecutors
spark.dynamicAllocation.minExecutors 1
spark.dynamicAllocation.maxExecutors infinity //共享的 spark 集群最好配置 maxExecutors
复制代码

并行度

Spark 并行度 = min( 任务数 = 分区数,总核数 )


一个参考值:分区数 = 总核心数的 2-3 倍

分区初始数量

分区数会影响 Spark 集群的并行度,下面有两种方式来计算分区数量


  1. 内存资源紧缺时: Math.round(inputDataSize/availableTaskMemoryMB()).toInt 其中 inputDataSize 为每个 task 的数据大小,可以从 Spark UI 上查看;availableTaskMemoryMB 即为上文计算的 用于 task 的执行内存大小

  2. 内存资源足够时:分区数量先设置为集群可用总 cores *2,然后逐步往上调,寻找一个最佳分区数(core 的整数倍)


什么是最佳分区数呢?执行时间最短就是最佳,此外还可以根据 Spark UI 判断


  • 分区数量太多的表现:executor cpu 内存利用率过低,过多 pending 的 task

  • 分区数量太少的表现:executor 空闲

分区调整

  • 分区数量调整:使用 repartition()可以调整分区数量,但会发生 shuffle,若减少分区,可以尝试使用coalesce()来避免 shuffle (一些特殊场景 repartition 更优,其增加的 shuffle 可能会减少其他地方的 shuffle,降低整体的时间)

  • 分区策略调整:若发生数据倾斜,可以通过调整合适的分区策略避免

Shuffle 调优

Shuffle 调优的目的是:避免 spill 到 disk 导致任务速度变慢


当在 Spark UI 观察到存在溢写时,一般有以下手段


  • 增加内存

  • 配置堆外内存

  • 增加分区以减少每个任务的数据量

  • 调整 shuffle 参数


相关配置如下



另外还可以优化代码(SQL or RDD API)防止 shuffle


  • Join 时广播小表(如使用 Broadcast Hash Join)

  • 尽量使用窄依赖而不是宽依赖

  • 使用 ReduceByKey 而不是 GroupbyKey

  • 在要进行宽依赖之前,或者进行完一系列复杂操作后,或进行完某些耗时操作后,persist RDD 进行缓存

Spark 调优工具

推荐使用一些 Spark 调优工具来帮助调优


  • Sparklens

  • Sparklint

  • Dr Elephant

参考文档


发布于: 刚刚阅读数: 2
用户头像

TiDB 社区官网:https://tidb.net/ 2021.12.15 加入

TiDB 社区干货传送门是由 TiDB 社区中布道师组委会自发组织的 TiDB 社区优质内容对外宣布的栏目,旨在加深 TiDBer 之间的交流和学习。一起构建有爱、互助、共创共建的 TiDB 社区 https://tidb.net/

评论

发布
暂无评论
Spark 性能调优_性能调优_TiDB 社区干货传送门_InfoQ写作社区