1.spark的内存模型

 (1)介绍:在执行spark的应用程序时,spark集群会启动driver和executor两种JVM进程。

   - driver为主控进程,负责创建sparkContext上下文对象,提交spark作业,并将作业转化为计算任务,在各个executor进程间协调任务的调度(一个
   - executor进程,负责为工作节点执行具体的计算任务,并将结果返回给driver,同时需要持久化RDD提供存储功能。(多个

 (2)spark的内存划分:

    1)整体划分:

      - (executor内部)JVM内部的堆内内存(对于JVM来说,就叫做堆内存)
      - (executor外部)JVM外部/操作系统的堆外内存。
     不论是堆内内存还是堆外内存都有执行内存和存储内存

    2)应用程序所占用的内存:

      - 执行内存(必须保证分配到足够的大小):全局变量、静态变量、task的计算时的数据内存区间、代码执行过程中所使用的临时空间、 Stage0和stage1之间的shuffle,这个shuffle过程存储的数据。
      - 存储内存:RDD的缓存、广播变量...

 (3)spark的堆内内存和堆外内存:

  作为一个JVM进程,executor的内存管理建立在JVM的内存管理之上,spark对JVM的堆内内存空间进行更为详细的分配,以充分利用内存空间。同时,spark引入了堆外内存和堆内内存,使之可以直接在工作节点的系统内存中开辟空间,进一步的优化了内存的空间。
spark调优之内存调优_第1张图片
 一个executor当中的所有task是共享堆内内存的。
 一个work中的多个executor中的多个task是共享堆外内存的。
注意:默认的spark是开启堆内内存的,配置参数:--executor-memory
但是默认的堆外内存是不开启的:

spark.memory.offHeap.enabled=true  #开启堆外内存
spark.memory.offHeap.size =1024    #分配堆外内存的大小。

 (4)spark的两种内存管理方式:

spark调优之内存调优_第2张图片
Spark中内存的管理是由以上图中的类完成的,MemoryManager是抽象的父类,具体的是下面的两个实现类:

  • StaticMemoryManager:静态内存管理(spark1.x)执行内存和存储内存互相不能占用
  • UnifiedMemoryManager :统一内存管理(spark2.x)执行内存和存储内存互相能占用
    不论是静态内存管理还是统一内存管理都有常用的六个方法:
    spark调优之内存调优_第3张图片

        1)静态内存管理(spark 1.x):

         在spark最初采用的静态内存管理机制下,存储内存、执行内存和其他内存的大小在spark应用程序运行期间均为固定的,但用户可以应用程序启动的时候进行配置。
    堆内内存
    spark调优之内存调优_第4张图片
    可用的存储内存StorageMemory =
    systemMaxMemory spark.storage.memoryFraction spark.storage.safetyFraction = 43.2%

可用的执行内存executionMemory=
systemMaxMemory spark.shuffle.memoryFraction spark.shuffle.safetyFraction = 16%
堆外内存
spark调优之内存调优_第5张图片
存储内存和执行内存各站50%

    2)统一内存管理(spark 2.x):

     Spark-1.6之后引入统一内存管理机制,与静态内存管理的区别在于存储内存和执行内存共享同一块空间,可以动态占用对方的内存。
堆内内存
spark调优之内存调优_第6张图片
堆外内存
spark调优之内存调优_第7张图片
动态占用机制
spark调优之内存调优_第8张图片

  • Execution内存不够用的时候,可以去storage内存中申请使用
  • Storage内存不够用的时候也可以去execution区域中声明使用
  • 但是storage内存在使用完占用execution内存后,需要归还,而execution内存在使用完storage内存时默认的是不归还的

2.spark的内存资源调优:

 (1)介绍:

   在开发完 Spark 作业之后,就该为作业配置合适的资源了。Spark 的资源参数,基本都可以 在 spark-submit 命令中作为参数设置。资源参数设置的不合理,可能会导致没有充分利用集群资源,作业运行会极其缓慢;或者设置的资源过大,队列没有足够的资源来提供,进而导致各种异常。因此我们必须对 Spark 作业的资源使用原理有一个 清晰的认识,并知道在 Spark 作业运行过程中,有哪些资源参数是可以设置的,以及如何设 置合适的参数值。

 (2)资源调优:

   所谓的 Spark 资源参数调优,其实主要就是对Spark运行过程中各个使用资源的地方,通过调节各种参数, 来优化资源使用的效率,从而提升 Spark 作业的执行性能。

--num-executors(only yarn):
参数说明该参数用于设置spark作业总共要用多少个executor进程执行。Driver在向yarn集群管理器申请资源时,yarn集群管理器会尽可能按照你的设置来在集群的各个工作节点上启动相应数量的executor,这个参数十分重要,如果不设置的话,默认 只会给你启动少量的 Executor 进程,此时你的 Spark 作业的运行速度是非常慢的。(如果是hdfs的话,那么一个block启动一个executor)
参数调优:设置太少或太多的 Executor 进程都不好。设置的太少,无法充分利用集群资源;设置的 太多的话,大部分队列可能无法给予充分的资源。

--executor-memory
参数说明该参数用于设置每一个executor进程的内存(堆内内存)。Executor的内存大小,很多时候直接决定了spark作业的性能,而且跟常见的JVM OOM异常,也有之间关系。
参数调优:申请的内存量最好不要超过资源队列最大 总内存的 1/3~1/2,避免你自己的 Spark 作业占用了队列所有的资源。

--executor-cores
参数说明该参数用于设置每一个executor进程的CPU core 的数量。这个参数决定了每个 Executor 进程并行执行 task 线程的能力。因为每个 CPU core 同一时间只能执行一个 task 线 程,因此每个 Executor 进程的 CPU core 数量越多,越能够快速地执行完分配给自己的所有 task 线程。
参数调优:Executor 的 CPU core 数量设置为 2~4 个较为合适。如果是跟他 人共享这个队列,那么 num-executors executor-cores 不要超过队列总 CPU core 的 1/3~1/2 左右比较合适,即每一个线程执行的task大约是2~4之间,最好的应该就是一个 cpu core 对应两到 三个 task。

--driver-memory
参数说明该参数用于设置 Driver 进程的内存
参数调优Driver 的内存通常来说不设置,或者设置 1G 左右应该就够了。唯一需要 注意的一点是,如果需要使用 collect 算子将 RDD 的数据全部拉取到 Driver 上进行处理,那 么必须确保 Driver 的内存足够大,否则会出现 OOM 内存溢出的问题。

spark.default.paralleism
参数说明:该参数用于设置每个 stage 的默认 task 数量。这个参数极为重要,如果不设 置可能会直接影响你的 Spark 作业性能。一个分区对应一个 task,也就是这个参数其实就是 设置 分区 的数量
参数调优:设置该参数为 num-executors
executor-cores 的 2~3 倍较为合适,比如 Executor 的总 CPU core 数量为 300 个,那么设置 1000 个 task 是可以的,此时可以充分地利用 Spark 集群的资源。
注意

  • 如果设置的local模式,没有executor,所有的任务都在driver中执行
  • 如果设置的是yarn/standalone的话,默认的开启2个task
  • 每一个stage中的task总数,是由这个stage中的最后一个RDD的分区数来决定的。

    spark.storage.memeoryFraction
    参数说明:该参数用于设置RDD持久化数据在executor内存中能占的比例,默认是0.6(存储内存)
    参数调优:参数的值可以适当提 高一些,保证持久化的数据能够容纳在内存中。避免内存不够缓存所有的数据,导致数据只 能写入磁盘中,降低了性能。当应用程序中需要的RDD的缓存比较小的时候,将这个参数调低一些。

    spark.shuffle.memeoryFraction
    参数说明:参数用于设置shuffle过程中一个task拉取到上一个stage的task的输出后,进行聚合操作时能够使用的executor内存比例。(执行内存的比例)
    参数调优:如果 Spark 作业中的 RDD 持久化操作较少,shuffle 操作较多时,建议降 低持久化操作的内存占比,提高 shuffle 操作的内存占比比例,避免 shuffle 过程中数据过多 时内存不够用,必须溢写到磁盘上,降低了性能。