Spark——系统调优

JVM层

  • 降低cache操作的内存占比

Spark中,堆内存被划分成了两块:

  1. 专门用来给RDD的cache、persist操作进行RDD数据缓存用的;
  2. 用来给spark算子函数的运行使用的,存放函数中自己创建的对象。

默认情况下,给RDD cache操作的内存占比是0.6,即60%的内存都给了cache操作了。但是问题是,如果某些情况下cache占用的内存并不需要占用那么大,

问题在于task算子函数中创建的对象过多,然后内存又不太大,导致了频繁的minor gc,甚至频繁full gc,导致spark频繁的停止工作。性能影响会很大。

针对上述这种情况,这个时候可以将其内存占比适当降低。怎么判断在什么时候调整RDD cache的内存占用比呢? 

其实通过Spark监控平台就可以看到Spark作业的运行情况了,包括每个task的运行时间、gc时间等等。如果发现task频繁的gc,就可以去调整cache的内存占用比了。

在代码中添加如下配置来设定:

SparkConf.set("spark.storage.memoryFraction","0.6") 
  • 堆外内存的调整
堆内存完全由 JVM 负责分配和释放,如果程序存在缺陷,有可能导致内存泄漏而溢出,抛出 OOM 异常: java.lang.OutOfMemoryError。
除了堆内存,Java 还可以使用堆外内存,也称直接内存(Direct Memory)。顾名思义,堆外内存是在 JVM Heap 之外分配的内存块,并不是 JVM 规范中定义的内存区域。
堆外内存可直接分配和释放,减少 GC 暂停时间,提高效率;可扩展,支持进程间共享,节省堆内存到堆外内存的拷贝等特点。如果程序存在缺陷,同样有可能导致堆外内存泄漏而溢出:OutOfDirectMemoryError
    • 问题提出

有时候,如果你的spark作业处理的数据量特别特别大,几亿数据量;然后spark作业一运行就会出现类似shuffle file cannot find,executor、task lost,out of memory(内存溢出)等这样的错误。

这是因为可能是说executor的堆外内存不太够用,导致executor在运行的过程中,可能会内存溢出;然后可能导致后续的stage的task在运行的时候,可能要从一些executor中去拉取shuffle map output文件,

但是executor可能已经挂掉了,关联的blockmanager也没有了;所以可能会报shuffle  output file not found;resubmitting task;executor lost 这样的错误;最终导致spark作业彻底崩溃。

上述情况下,就可以去考虑调节一下executor的堆外内存。也许就可以避免报错;此外,有时,堆外内存调节的比较大的时候,对于性能来说,也会带来一定的提升。

    • 解决方案

在spark-submit脚本里面添加如下配置。默认情况下,这个堆外内存上限大概是300多M;我们通常项目中真正处理大数据的时候,这里都会出现问题导致spark作业反复崩溃无法运行;

此时就会去调节这个参数,到至少1G或者更大的内存。通常这个参数调节上去以后,就会避免掉某些OOM的异常问题,同时呢,会让整体spark作业的性能,得到较大的提升。

--conf spark.yarn.executor.memoryOverhead=2048

注:看名字,顾名思义,以上设置针对的是基于yarn的提交模式

  • 连接等待时长的调整
    • 问题提出

executor会优先从自己本地关联的BlockManager中获取某份数据。如果本地block manager没有的话,那么会通过TransferService,去远程连接其他节点上executor的block manager去获取。

而此时上面executor去远程连接的那个executor,因为task创建的对象特别大,特别多, 频繁的让JVM堆内存满溢,正在进行垃圾回收。

而处于垃圾回收过程中,所有的工作线程全部停止,相当于只要一旦进行垃圾回收,spark / executor停止工作,无法提供响应。 此时呢,就会没有响应,无法建立网络连接,会卡住。

spark默认的网络连接的超时时长是60s;如果卡住60s都无法建立连接的话,那么就宣告失败了。碰到一种情况,有时候报错信息会出现一串类似file id not found,file lost的错误。

这种情况下,很有可能是task需要处理的那份数据的executor在正在进行gc。所以拉取数据的时候,建立不了连接。然后超过默认60s以后,直接宣告失败。

几次都拉取不到数据的话,可能会导致spark作业的崩溃。也可能会导致DAGScheduler,反复提交几次stage。TaskScheduler,反复提交几次task。大大延长我们的spark作业的运行时间。

    • 解决方案

在spark-submit脚本中添加如上参数,调节这个值比较大以后,通常来说,可以避免部分的偶尔出现的某某文件拉取失败,某某文件lost掉的错误。

--conf spark.core.connection.ack.wait.timeout=300
  • GC—G1参数
spark.executor.extraJavaOptions -Dtag=mobius.optimize 
-XX:+UseG1GC   #指定垃圾收集器
-XX:G1HeapRegionSize=32m #设置的 G1 区域的大小
-XX:MaxGCPauseMillis=200 #每次GC的目标停顿时间(默认值是 200 毫秒)。这是一个软目标,并且JVM将尽最大的努力来实现它,但不保证每次STW都严格小于该值
-XX:G1NewSizePercent=5 #年轻代大小最小值的堆百分比(默认值是 Java 堆的 5%)。G1会根据MaxGCPauseMillis动态调整年轻代区的大小,但要保证占比肯定大于或等于该值
-XX:G1MaxNewSizePercent=60 #轻代大小最大值的堆大小百分比(默认值是 Java 堆的 60%)
-XX:MaxTenuringThreshold=1 #一个对象最多经历多少次young GC会被放入老年代(默认值是 15)
-XX:ParallelGCThreads=10 #设置在垃圾收集器的并行阶段使用的线程数。缺省值随运行JVM的平台而异
-XX:ConcGCThreads=8 #并发垃圾收集器将使用的线程数。缺省值随运行JVM的平台而异

-XX:InitiatingHeapOccupancyPercent=45 #设置触发并发标记周期的 Java 堆占用率阈值(默认值占用率是整个 Java 堆的 45%)。G1开始进行并发标记的阈值,并发标记之后,就会进入Mixed GC周期
-XX:G1MixedGCCountTarget=32 #一个Mixed GC 周期中最多执行多少次Mixed GC
-XX:G1OldCSetRegionThresholdPercent=5 #一次Mixed GC 最多能回收多大的 OldRegion 空间
-XX:G1ReservePercent=10 #设置作为空闲空间的预留内存百分比,以降低目标空间溢出的风险(默认值是 10%)

-XX:-UseGCOverheadLimit #取消垃圾回收时的内存限制(减号)
-XX:+UnlockDiagnosticVMOptions #解锁实验性虚拟机标志
-XX:+G1SummarizeConcMark

-verbose:gc  
-XX:+PrintGC   #打印GC日志
-XX:+PrintGCDetails #打印详细GC日志
-XX:+PrintGCDateStamps #打印可读的日期时间戳 比如2018-11-12 17:22:15
-XX:+PrintGCApplicationStoppedTime #打印应用停留时间
-XX:+PrintHeapAtGC #在GC前后打印GC日志
-XX:+PrintTenuringDistribution #打印各个年代对象分布
-XX:+PrintAdaptiveSizePolicy #打印自适应大小策略
-XX:+PrintFlagsFinal #打印java对象引用
-XX:+PrintReferenceGC #打印垃圾对象的引用数量
-Xloggc:/tmp/spark.gc.log #指定GC日志路径 (如果不指定,默认输出到各worker节点的$SPARK_HOME/work/$app_id/$executor_id/stdout中)
-XX:+UseGCLogFileRotation #GC日志文件切割
-XX:NumberOfGCLogFiles=10 #GC日志文件数量
-XX:GCLogFileSize=128M #GC日志文件大小(单个)
-XX:+PrintSafepointStatistics
-XX:PrintSafepointStatistics=1
-XX:PrintFLSStatistics=1

引用:

原文地址:https://www.cnblogs.com/caoweixiong/p/13498278.html