Spark——系统调优
JVM层
- 降低cache操作的内存占比
Spark中,堆内存被划分成了两块:
- 专门用来给RDD的cache、persist操作进行RDD数据缓存用的;
- 用来给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")
- 堆外内存的调整
-
- 问题提出
有时候,如果你的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 \
引用: