Spark 内存调优以及 JVM 调优 之二

539 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

JVM 参数调优

步骤:

  1. 在 添加打印 GC 日志的参数:spark.executor.extraJavaOptions=-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
  2. 提交 spark 应用,查看花费时间最多的 stage 中,GC 花费时间最大的 task 任务的日志.
  3. 查看 GC 日志: 3.1 如果一个task 任务完成之前发生了多次FullGC,说明没有分配足够多的内存,如果机器内存还足够的话,可以增加executor-memory 参数的值.同时,在代码中酌情较少 RDD 的缓存.在一个,jvm 的垃圾收集器使用更高效的 G1收集器 3.2 增加年轻代的堆内存空间,这样可以减少年轻代内存区域发生GC的次数,从而减少本不应该到老年代的对象的GC 年龄,这样可以减少进入老年代的对象,从而减少老年代发生 fullGC 的频率.设置新生代堆大小的参数:-Xmn,设置时,可以根据 Eden区的大小进行设置,大概为 Eden 区大小的4/3倍. 3.3 如果源数据是从HDFS读取,则可以使用从HDFS读取的数据块的大小来估计任务使用的内存量。请注意,解压缩块的大小通常是块大小的2或3倍。因此,如果我们希望有3或4个任务的工作空间,并且HDFS块大小为128 MB,我们可以估计Eden的大小4*3*128MB,则新生代的大小为4*3*128*4/3MB

内存调优建议

  1. 使用高效的数据序列化类KryoSerializer
    • 参数:spark.serializer:org.apache.spark.serializer.KryoSerializer
  2. 设计数据结构时优先使用对象数组和基本类型,使用到集合类库时,可以使用 fastutil 库.
  3. 合理的使用内存管理以及内存管理参数设置
    1. 内存管理模型选择:
      1. 如果我们的spark 应用计算比较复杂,没法准确的预估计算和存储所占的比例,这时我们可以依赖UnifiedMemoryManager管理模型计算和存储软边界的特性选择使用UnifiedMemoryManager,这也是spark1.6之后系统默认的.
      2. 而如果我们 spark 应用计算业务逻辑时需要更大的存储空间,比如代码逻辑中使用频繁的使用广播变量,或者RDD 的 血统链比较长,需要使用 rdd 的 persist()等操作,这种情况下可以选择使用StaticMemoryManager的固定内存管理模型比较好,并且需要适当的spark.storage.memoryFraction参数系数.
    2. 在使用StaticMemoryManager管理内存时,如果 spark 应用用到了大量的广播或者 RDD 缓存,可以增加spark.storage.memoryFraction参数的值.反之,如果计算比较复杂,计算的过程中很少将 RDD 进行缓存,可以减小spark.storage.memoryFraction的值,同时增加spark.shuffle.memoryFraction的值.
    3. 在使用UnifiedMemoryManager管理内存时,如果计算比较复杂,可以将参数spark.memory.fraction稍微调大,同时调整spark.memory.storageFraction的值,默认是0.5,可以根据实际情况调整为如0.4(虽然在计算的过程中可以进行相互借用)
  4. jvm 参数优化 yarn下:

--conf spark.yarn.executor.memoryOverhead=2048 单位M

standalone下:

--conf spark.executor.memoryOverhead=2048单位M