Spark优化总结(三)——调参

27 篇文章 3 订阅
6 篇文章 0 订阅

Spark优化总结(三)——调参

前言

  • 不要期待修改一个参数能够像魔法一样立马得到神奇的好效果!(某些时候效果确实很棒^_^)你应当把参数看作一道菜中的调味品,能够丰富味道,但主要还是得靠原材料的质量与炒菜的技艺。
  • 开发Spark应用时,应当先优化好你的应用代码,再来思考调参优化(必要的参数的除外)。
  • 调参是一个比较复杂的主题,不同的环境、不同的代码都会导致同样的参数产生不同的效果。建议尽量在确定您的生产环境情况后、在优化好存在明显问题的代码后,再做调参测试。
  • 下面会列出开发中常用的部分参数,并加以解释,以作参考

简单示例

  • 一个Spark任务提交示例

    spark-submit \
    --queue test_queue --master yarn --deploy-mode cluster \
    --num-executors 10 --executor-memory 8G --executor-cores 4 \
    --driver-memory 4G --driver-cores 2 \
    --conf spark.network.timeout=300 \
    --conf spark.locality.wait=9s \
    --class com.skey.spark.app.MyApp /home/jerry/spark-demo.jar 
    
  • 参数解释

    • –queue test_queue 将任务提交到YARN上面的test_queue队列
    • –master yarn 将任务运行在YARN上面
    • –deploy-mode cluster 指定cluster模式运行
    • –num-executors 10 指定启动10个Executor进程
    • –executor-memory 8G 每个Executor分配8G内存
    • –executor-cores 4 每个Executor分配4个core,一个core对应一个线程
    • –driver-memory 4G 为Driver进程分配4G内存
    • –driver-cores 2 为Driver分配2个core,一个core对应一个线程
    • –conf spark.network.timeout=300s 设置集群内网络通信延迟为300s
    • –conf spark.locality.wait=9s 设置数据本地化加载等待时间
    • –class com.skey.spark.app.MyApp 指定需要运行的spark应用类
    • /home/jerry/spark-demo.jar 指定需要运行的jar包

常用参数

  • 下面列出的参数以Spark 2.4.3的configuration为标准
  • spark.memory.fraction
    • 默认值: 0.6
    • 解释: 新版内存管理(UnifiedMemoryManager)模式中,execution和storage的总可用内存比例(它们之间可以相互借用内存)。内存值 = (总内存值 - 300M) * spark.memory.fraction(0.6)。
    • 建议: 如果采用默认的新版内存管理模式的话,适当调大该值,可以有效的减少磁盘spill和踢除block的概率
  • spark.memory.storageFraction
    • 默认值: 0.5
    • 解释:新版内存管理(UnifiedMemoryManager)模式中,storage的内存比例,剩余的则是execution的内存。
    • 建议:execution更多,例如shuffle更多,那么调小该值,性能更好。如果缓存较多,可以调大该值。
  • spark.memory.offHeap.enabledspark.memory.offHeap.size
    • 默认值: false与0
    • 解释:新版内存管理(UnifiedMemoryManager)模式中,是否开启堆外内存、堆外内存的大小
    • 建议:开启堆外内存可以有效的避免GC,建议尝试开启,给一个适当的值,例如堆内存的10%。需要注意的是堆内、堆外内存是分开算的。
  • spark.memory.useLegacyMode
    • 默认值: false
    • 解释: 该参数用于控制是否使用遗留的内存管理模式(StaticMemoryManager,也就是钨丝计划之前的内存模型,即1.6版本以前)。遗留模式下,内存由spark.storage.memoryFraction(0.6) + spark.shuffle.memoryFraction(0.2) + 系统默认(0.2) 组成。
    • 建议: 刚开始开发应用时,可以不开启,直接采用默认的内存管理器,进行动态分配即可。一旦开发完成,可以尝试调试shuffle、storage的比例,如果能够确定其值时,可以改为true,以减少动态分配、spill、剔除block的概率。另外动态分配模式下execution从storage借来的内存不管还需不需要用都不会还给storage,可能会导致部分问题。
  • spark.storage.memoryFraction
    • 默认值: 0.6
    • 解释: spark.memory.useLegacyMode为true时有效,用于控制缓存部分的内存
    • 建议: 如果缓存数据比较大,可以调大该参数
  • spark.shuffle.memoryFraction
    • 默认值: 0.2
    • 解释: spark.memory.useLegacyMode为true时有效,用于控制shuffle部分
    • 建议: 如果频繁发生spill(溢写),可以调大该参数。如果你的应用缓存用的很少,请使劲调大该参数^_^
  • spark.storage.unrollFraction
    • 默认值: 0.2
    • 解释: spark.memory.useLegacyMode为true时有效,用于缓存的数据的序列化/反序列化,占spark.storage.memoryFraction的20%
    • 建议: 可以不调
  • spark.executor.memoryOverhead
    • 默认值: executorMemory * 0.10,最低384M
    • 解释: 设置每个Executor的堆外内存,主要用于JVM自身,字符串, NIO Buffer等开销。YARN模式下,等于 Container内存 - Executor内存。(没在官网找到该参数,源码中存在该参数)
    • 建议: Spark使用的是基于nio的,nio使用直接内存区效率非常高,可以适当的分配部分内存到堆外区。
  • spark.locality.wait
    • 默认值: 3s
    • 解释: 用于指定数据本地化等待时长,包括3个子参数(spark.locality.wait.node、spark.locality.wait.process、spark.locality.wait.rack)用于更细致的调节。
    • 建议: 通常统一调整spark.locality.wait即可。可以适当加长等待时间,特别是数据量比较大时,如果能够本地化处理,效果更佳。
  • spark.network.timeout
    • 默认值: 120s
    • 解释: spark内存通信的网络延时
    • 建议: 如果spark应用处理比较耗时,那么可以适当调大该参数(例如300s),防止延时导致的报错
  • spark.default.parallelism
    • 默认值: 无
    • 解释: 用于指定RDD的shuffle操作的分区数,例如reduceByKey、join等。调用shuffle算子时,优先使用算子指定的分区数,否则使用spark.default.parallelism的值,如果还是没值,则使用父RDD的分区数的值。对sql中的shuffle无效。
    • 建议: 如果你想精细化控制,愿意为每个shuffle算子添加parallelism值,那么不用设置^_^。建议可以先设置一个值(例如总core的2-3倍),然后代码中有需要调整的就向shuffle算子传参,覆盖本次的默认值。(不要误信网上瞎说的什么并行度)
  • spark.sql.shuffle.partitions
    • 默认值: 200
    • 解释: 用于指定SparkSQL执行shuffle时的分区数。(没在官网找到该参数)
    • 建议: SQL在执行shuffle时,如果默认200小于你的总core数,就会浪费资源了。建议设置为总core的2-3倍。
  • spark.jars
    • 默认值: 无
    • 解释: 指定jar包(用逗号分隔),会将其传到Driver、Executor端
    • 建议: 除了spark lib下的包,其他额外引用的库建议都指定。(client模式下,只有Driver会用到的库,可以不传)
  • spark.jars.excludes
    • 默认值: 无
    • 解释: 排除不要的包,防止依赖冲突。格式 groupId:artifactId
    • 建议: …
  • spark.executor.userClassPathFirst与spark.driver.userClassPathFirst
    • 默认值: false
    • 解释: 用于指定是否优先加载用户指定的jar
    • 建议: 因为存在用户指定的jar与spark默认库下jar包可能冲突的问题,所以当冲突时,如果你能确定你的jar没问题,那么可以设置为true。(之前遇到一个华为的bug就是这样)
  • spark.driver.maxResultSize
    • 默认值: 1g
    • 解释: 数据传到Driver端的最大量,例如collect、take操作
    • 建议: 有时候可能需要直接拉取大量的数据,可以根据数据量来调整该参数。
  • spark.reducer.maxSizeInFlight
    • 默认值: 48m
    • 解释: 每个reduce任务拉取数据的最大量
    • 建议: 每个输出端都需要创建一个buffer,消耗比较大。如果内存比较大,可以提高该值,拉取速度会加快。
  • spark.shuffle.file.buffer
    • 默认值: 32k
    • 解释: shuffle时,数据输出到文件的buffer大小,写满buffer后,数据会溢写到磁盘。
    • 建议: 调大该值,可以降低溢写到磁盘的次数(减少I/O次数),提高性能。内存充足的话,可以调大,例如64k。
  • spark.shuffle.io.maxRetries
    • 默认值: 3
    • 解释: shuffle时,read端从write端拉取数据的重试次数
    • 建议: 因为GC、网络延迟等问题可能会导致拉取失败,可以适当提高重试次数,防止意外。
  • spark.shuffle.io.retryWait
    • 默认值: 5s
    • 解释: spark.shuffle.io.maxRetries每次重试需要等多久
    • 建议: 可以加大,提高稳定性
  • spark.shuffle.manager
    • 默认值: sort
    • 解释: 用于管理shuflle文件输出到磁盘的方式。建议百度看看详细流程。1.2版以前默认HashShuffleManager, 之后默认是SortShuffleManager。Spark 2后不再有该参数,直接是SortShuffleManager,默认会对数据进行排序。
    • 建议: 可以通过spark.shuffle.sort.bypassMergeThreshold调整是否排序。
  • spark.shuffle.sort.bypassMergeThreshold
    • 默认值: 200
    • 解释: shffle时,如果read task数小于200(并且是非预聚合的Shuffle,例如reduceByKey是预聚合型),会启用bypass机制:不会进行排序操作,最后会合并task产生的文件,并创建索引
    • 建议: 不需要排序时,可以提高该参数,以大于你的shuffle read task数。(会有不错的效率提升,我的一个应用降低了20%时间)
  • spark.kryo.registrationRequired
    • 默认值: false
    • 解释: 是否强制kryo序列化
    • 建议: 如果为false,kyro需要为每个对象写未注册类的类名,会造成显著的性能开销。建议设置为true。(某些朋友对于JVM报出的有的类不知道怎么注册,在这里建议复制报错的类,使用Class.forName(“类”))
  • spark.rdd.compress
    • 默认值: false
    • 解释: 是否压缩序列化的RDD数据
    • 建议: 开启可以减少内存,但是解压会增加CPU消耗时间
  • spark.scheduler.mode
    • 默认值: FIFO
    • 解释: 同一个SparkContext内的调度机制,包括FIFO、FAIR
    • 建议: 一般使用较少。FIFO,先进先出,优先执行先提交的,有空闲的再给后面的job。FAIR,公平分配资源,为每个可以并行执行的job平均分配计算资源。
  • spark.streaming.backpressure.enabled
    • 默认值: false
    • 解释: 是否启用背压机制,控制SparkStream接收数据的速率。
    • 建议: 流式处理中,处理速度如果较慢,会导致来的数据不断积压。启用后,Spark可以自己动态根据处理能力调整接收数据的量。如果存在积压情况,建议启用。另外还有几个参数,用于细节控制,不建议调整。
  • spark.streaming.blockInterval
    • 默认值: 200ms
    • 解释: 每批处理的task数 = 批处理间隔 / blockInterval
    • 建议: blockInterval越大,task则越少,会导致部分core没有使用。可以根据你的core的量,适当降低该参数。(官方建议blockInterval最小值约为50ms)
  • spark.streaming.concurrentJobs
    • 默认值: 1
    • 解释: 一个SparkStream应用内可以同时运行多少个job。(没在官网找到该参数)
    • 建议: 如果你分配的core比较多,每批的task数比较少(还可能处理时间比较长),空闲的core比较浪费,那么可以调高该参数,同时运行后面的job(如果2个job之间没有前后关联的话)
  • spark.driver.extraJavaOptionsspark.executor.extraJavaOptions
    • 默认值: 无
    • 解释: 用于指定JVM参数
    • 建议: 看JVM调参部分

JVM调参

  • 一般不要先调JVM,开发中的大多数问题都是代码质量不好导致的,先去看代码、业务逻辑是否有问题,优化、优化、再优化……^_^
  • 然后,确定运行环境,再来调整JVM参数
  • 注意,运行时默认值会随着JVM版本、系统环境而改变,请用 java -XX:+PrintFlagsFinal -version命令查看JVM最终参数
  • 查看GC情况
    • 查看每个节点的GC
      • 添加-XX:PrintGCDetails,让每个节点打印GC日志,需要在每个节点分别查看。On YARN的话,可以点击WebUI界面查看每个节点日志。
    • 想看Spark应用整体的吞吐量?
      • 每个应用的Spark WebUI有展示,直接在这儿看就可以了。如果GC时间超过10%,那么说明你的应用需要优化了!
  • 关于GC的选择(Java 8)
    • 批处理,需要吞吐量较高?用 -XX:+UseParallelGC
    • 流式处理,需要数据具有较高的实时性?用 -XX:+UseConcMarkSweepGC 或 G1,内存大于6G用G1
    • 琢磨不定?选 ParallelGC
    • 批处理时想用G1?可以试试,但是吞吐量没有ParallelGC好(Java 8)
  • 并行垃圾收集器参数(ParallelGC)
    • 吞吐量优先为准则
    • -XX:+UseParallelGC 设置年轻代使用ParallelGC,早期版本中老年代会默认使用SerialGC。
    • -XX:+UseParallelOldGC 设置老年代也使用ParallelOldGC,Java1.6后开始支持(我的系统中是默认开启的,你的不一定)
    • -XX:ParallelGCThreads=8 设置并行收集垃圾的线程数为8,一般同CPU核个数
    • -XX:MaxGCPauseMillis=100 设置ParallelGC在年轻代单次回收的最长耗时为100毫秒。如果耗时超过该值,JVM会自动调整年轻代内存大小,以适应该值。可以调大该值,例如500,以保证吞吐量。
    • -XX:GCTimeRatio=99 设置垃圾回收时间占总时间的百分比最高为1%,公式为1/(1+99),即吞吐量为99%。默认为99。
    • -XX:+UseAdaptiveSizePolicy 启用自适应策略。JVM会自动调整年轻代Eden区与Survivor区大小的比例,以适应GCTimeRatio的值。建议开启。
  • 并发垃圾收集器参数(ConcMarkSweepGC)
    • 响应时间优先为准则
    • -XX:+UseConcMarkSweepGC 设置老年代使用ConcMarkSweepGC,年轻代默认使用ParNewGC。
    • -XX:+UseParNewGC 设置年轻代为ParNewGC。JDK5以上使用CMS时,默认年轻代会采用ParNewGC。
    • -XX:+UseCMSCompactAtFullCollection 开启压缩整理(默认),压缩整理用于消除内存碎片化问题(CMS采用的标记清除算法,默认每次FullGC后进行整理)
    • -XX:CMSFullGCsBeforeCompaction=1 设置间隔1次FullGC后(默认为0,表示每次),开始对内存进行压缩整理。
    • -XX:CMSInitiatingOccupancyFraction=70 设置老年代内存使用70%后开始进行并发垃圾收集
  • 内存调整常用参数
    • 常用比例
            young    | old
      eden | s0 | s1 | tenured
      ------------------------
             1       |  2
      8    | 1  | 1  | 
      
    • -Xmx4G 设置JVM最大堆内存为4G
    • -Xms4G 设置JVM初始堆内存为4G,一般设为与-Xmx同样大即可(避免重新分配内存)
    • -Xmn1G 设置年轻代内存大小为1G。堆内存 = 老年代 + 年轻代,因此年轻代与老年代之间要根据GC情况取得一个平衡。例如MinorGC较多,可以调大年轻代,MajorGC较多可以调大老年代(即调小年轻代)。
    • -XX:NewSize=512M 设置年轻代初始大小为512M,一般设为与MaxNewSize同样大即可。(不建议使用,直接用-Xmn)
    • -XX:MaxNewSize=1024M 设置年轻代最大大小为1024M。(不建议使用,直接用-Xmn)
    • -XX:MetaspaceSize=128M 设置元信息区(永久代)初始大小为256M,一般设为与MaxMetaspaceSize同样大即可。(Java8以前叫做-XX:PermSize)
    • -XX:MaxMetaspaceSize=256M 设置元信息区(永久代)最大大小为256M。(Java8以前叫做-XX:MaxPermSize)
    • -XX:NewRatio=2 设置年轻代与老年代的大小比例为 1 : 2 (Java8默认为2)
    • -XX:SurvivorRatio=8 设置年轻代中Eden区与Survivor的大小比例为 8 : 1 : 1 (enden : survivor0 : survivor1)
    • -Xss256K 设置每个线程私有栈的的大小为256K(Java8默认为1M)。线程越多内存占用越大,需要启用超多线程时,可以调低该参数,例如128k。
    • -XX:MaxDirectMemory=100M 设置最大堆外内存
  • 其他常用参数
    • -XX:+PrintGCDetails 打印GC详细信息
    • -XX:+PrintGCTimeStamps 打印GC时的时间戳
    • -Xloggc:/home/jerry/logs/gc.log 设置GC日志输出目录
    • -XX:+HeapDumpOnOutOfMemoryError 当发生内存溢出异常时,dump出堆信息
    • -XX:HeapDumpPath=./my_java.hprof 指定dump堆信息的输出路径
    • -XX:MaxTenuringThreshold=8 设置对象在年轻代经历垃圾回收仍然存活8次后进入老年代。最大值15(因为JVM用4bit表示该值),默认值15(使用CMS时,默认为6)
    • -XX:+PrintFlagsFinal 打印JVM最终参数(例如 java -XX:+PrintFlagsFinal -version)
    • -XX:+PrintFlagsInitial 打印JVM初始参数(最终值会随环境改变)

调优-官方文档

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
很高兴为您介绍Spark性能优化指南——高级篇。 1. 数据倾斜问题 数据倾斜是指在Spark任务中,某些分区的数据量远远大于其他分区,导致任务执行时间过长。解决数据倾斜问题的方法包括: - 使用随机前缀或哈希分桶等方式对数据进行分区,使数据分布更加均匀。 - 对于reduceByKey等聚合操作,使用combineByKey或aggregateByKey等方法,避免数据倾斜。 - 对于join操作,使用broadcast变量或者将小表缓存到内存中,避免数据倾斜。 2. 内存管理 Spark的内存管理对于任务的性能有着重要的影响。以下是一些内存管理的优化方法: - 调整executor内存分配比例,避免OOM错误。 - 合理设置内存管理参数,如spark.memory.fraction、spark.memory.storageFraction等。 - 使用持久化存储,如cache或persist方法,避免重复计算和数据丢失。 3. 磁盘IO 磁盘IO是Spark任务中的瓶颈之一。以下是一些优化磁盘IO的方法: - 使用本地磁盘而非网络磁盘,避免网络IO带来的延迟。 - 使用压缩算法,如Snappy或LZ4,减少磁盘IO的数据量。 - 对于shuffle操作,使用Tungsten排序等优化算法,减少磁盘IO的次数。 4. 并行度 并行度是指任务中可以同时执行的任务数。以下是一些优化并行度的方法: - 调整任务的并行度,使任务能够充分利用集群资源。 - 对于shuffle操作,调整reduce任务的数量,避免过多的reduce任务导致性能下降。 - 对于数据量较大的任务,使用分区并行执行,避免单个任务的执行时间过长。 5. 网络传输 网络传输是Spark任务中的另一个瓶颈。以下是一些优化网络传输的方法: - 调整网络传输的缓存大小,使数据传输更加高效。 - 使用序列化算法,如Kryo或Java序列化,减少网络传输的数据量。 - 对于shuffle操作,使用Tungsten排序等优化算法,减少网络传输的数据量。 希望以上内容能够帮助您更好地优化Spark任务的性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值