
<h4 style="color: rgb(52,73,94);"> <span>1、常规性能调优一:最优资源配置</span> </h4> <p style="color: rgb(52,73,94);"> <span>Spark性能调优的第一步,就是为任务分配更多的资源,在一定范围内,增加资源的分配与性能的提升是成正比的</span><span> ,实现了最优的资源配置后,在此基础上再考虑进行后面论述的性能调优策略。</span> </p> <p style="color: rgb(52,73,94);"> <span>资源的分配在使用脚本提交Spark任务时进行指定,标准的Spark任务提交脚本如代码清单所示</span> </p> <p style="color: rgb(52,73,94);"> <span>可以进行分配的资源如下表所示</span> </p> <div style="padding: 0.0px;"> <tbody> <tr> <th style="text-align: left;background-color: rgb(242,242,242);"> <span><span>名称</span></span> </th> <th style="text-align: left;background-color: rgb(242,242,242);"> <span><span>说明</span></span> </th> </tr> </tbody> <tbody> <tr> <td> <span><span><strong><span>--num-executors</span></strong></span></span> </td> <td> <span><span>配置Executor的数量</span></span> </td> </tr> <tr> <td> <span><span><strong><span>--driver-memory</span></strong></span></span> </td> <td> <span><span>配置Driver内存(影响不大)</span></span> </td> </tr> <tr> <td> <span><span><strong><span>--executor-memory</span></strong></span></span> </td> <td> <span><span>配置每个Executor的内存大小</span></span> </td> </tr> <tr> <td> <span><span><strong><span>--executor-cores</span></strong></span></span> </td> <td> <span><span>配置每个Executor的CPU core数量</span></span> </td> </tr> </tbody> </div> <p style="color: rgb(52,73,94);"> <span>调节原则:</span><span>尽量将任务分配的资源调节到可以使用的资源的最大限度</span><span> </span> </p> <p style="color: rgb(52,73,94);"> <span>对于具体资源的分配,分别讨论Spark的两种Cluster运行模式:</span> </p> <p style="color: rgb(52,73,94);"> <span>第一种是</span><span>Spark Standalone模式</span><span>,你在提交任务前,一定知道或者可以从运维部门获取到你可以使用的资源情况,在编写submit脚本的时候,就根据可用的资源情况进行资源的分配,比如说集群有15台机器,每台机器为8G内存,2个CPU core,那么就指定15个Executor,每个Executor分配8G内存,2个CPU core。</span> </p> <p style="color: rgb(52,73,94);"> <span>第二种是</span><span>Spark Yarn模式</span><span>,由于Yarn使用资源队列进行资源的分配和调度,在表写submit脚本的时候,就根据Spark作业要提交到的资源队列,进行资源的分配,比如资源队列有400G内存,100个CPU core,那么指定50个Executor,每个Executor分配8G内存,2个CPU core。</span> </p> <p style="color: rgb(52,73,94);"> <span>对上表中的各项资源进行了调节后,得到的性能提升如下表所示</span> </p> <div style="padding: 0.0px;"> <tbody> <tr> <th style="text-align: left;background-color: rgb(242,242,242);"> <span><span>名称</span></span> </th> <th style="text-align: left;background-color: rgb(242,242,242);"> <span><span>解析</span></span> </th> </tr> </tbody> <tbody> <tr> <td> <span><span><strong><span>增加Executor个数</span></strong></span></span> </td> <td> <span><span>在资源允许的情况下,增加Executor的个数可以提高执行task的并行度</span><span>。比如有4个Executor,每个Executor有2个CPU core,那么可以并行执行8个task,如果将Executor的个数增加到8个(资源允许的情况下),那么可以并行执行16个task,此时的并行能力提升了一倍。</span></span> </td> </tr> <tr> <td> <span><span><strong><span>增加每个Executor的CPU core个数</span></strong></span></span> </td> <td> <span><span>在资源允许的情况下,增加每个Executor的Cpu core个数,可以提高执行task的并行度</span><span>。比如有4个Executor,每个Executor有2个CPU core,那么可以并行执行8个task,如果将每个Executor的CPU core个数增加到4个(资源允许的情况下),那么可以并行执行16个task,此时的并行能力提升了一倍</span></span> </td> </tr> <tr> <td> <span><span><strong><span>增加每个Executor的内存量</span></strong></span></span> </td> <td> <span><span>在资源允许的情况下,增加每个Executor的内存量以后,对性能的提升有三点:1. </span><span>可以缓存更多的数据(即对RDD进行cache)</span><span>,写入磁盘的数据相应减少,甚至可以不写入磁盘,减少了可能的磁盘IO;2. </span><span>可以为shuffle操作提供更多内存</span><span>,即有更多空间来存放reduce端拉取的数据,写入磁盘的数据相应减少,甚至可以不写入磁盘,减少了可能的磁盘IO;3. </span><span>可以为task的执行提供更多内存</span><span>,在task的执行过程中可能创建很多对象,内存较小时会引发频繁的GC,增加内存后,可以避免频繁的GC,提升整体性能。</span></span> </td> </tr> </tbody> </div> <p style="color: rgb(52,73,94);"> <span><strong><span>生产环境Spark submit脚本配置</span></strong></span> </p> <p style="color: rgb(52,73,94);"> <span>参数配置参考值:</span> </p> <p style="color: rgb(52,73,94);"> <span><span><strong><span style="color: inherit;">旧版本Spark资源调优情况</span></strong></span></span><span> </span> </p> <p style="color: rgb(52,73,94);"> <span>1)</span><span><strong><span>资源运行情况</span></strong></span> </p> <p style="color: rgb(52,73,94);"> <img alt="" src="https://uploadfiles.nowcoder.com/images//_88/6DCDC267EEC4727E721E955DC"> </p> <p style="color: rgb(52,73,94);"> <span>2)</span><span><strong><span>资源运行中的集中情况</span></strong></span> </p> <p style="color: rgb(52,73,94);"> <span>(1)实践中跑的Spark job,有的特别慢,查看CPU利用率很低,可以尝试减少每个executor占用CPU core的数量,增加并行的executor数量,同时配合增加分片,整体上增加了CPU的利用率,加快数据处理速度</span> </p> <p style="color: rgb(52,73,94);"> <span>(2)发现某job很容易发生内存溢出,我们就增大分片数量,从而减少了每片数据的规模,同时还减少并行的executor数量,这样相同的内存资源分配给数量更少的executor,相当于增加了每个task的内存分配,这样运行速度可能慢了些,但是总比OOM强</span> </p> <p style="color: rgb(52,73,94);"> <span>(3)数据量特别少,有大量的小文件生成,就减少文件分片,没必要创建那么多task,这种情况,如果只是最原始的input比较小,一般都能被注意到;但是,如果是在运算过程中,比如应用某个reduceBy或者某个filter以后,数据大量减少,这种低效情况就很少被留意到</span> </p> <p style="color: rgb(52,73,94);"> <span>3)</span><span><strong><span>运行资源优化配置</span></strong></span> </p> <p style="color: rgb(52,73,94);"> <span> 一个CPU core同一时间只能执行一个线程。而每个Executor进程上分配到的多个task,都是以每个task一条线程的方式,多线程并发运行的 </span> </p> <p style="color: rgb(52,73,94);"> <span>一个应用提交的时候设置多大的内存?设置多少Core?设置几个Executor?</span> </p> <p style="color: rgb(52,73,94);"> <span>1、运行资源优化配置 - </span><span><strong><span>num-executors</span></strong></span><span> </span> </p> <p style="color: rgb(52,73,94);"> <span><strong><span>参数说明</span></strong></span><span>:该参数用于</span><span><strong><span>设置Spark作业总共要用多少个Executor进程来执行</span></strong></span><span>。Driver在向YARN集群管理器申请资源时,YARN集群管理器会尽可能按照你的设置来在集群的各个工作节点上,启动相应数量的Executor进程。这个参数非常之重要,如果不设置的话,默认只会给你启动少量的Executor进程,此时你的Spark作业的运行速度是非常慢的</span> </p> <p style="color: rgb(52,73,94);"> <span><strong><span>参数调优建议</span></strong></span><span>:每个Spark作业的运行</span><span><strong><span>一般设置50~100个左右的Executor进程比较合适</span></strong></span><span>,设置太少或太多的Executor进程都不好。设置的太少,无法充分利用集群资源;设置的太多的话,大部分队列可能无法给予充分的资源</span> </p> <p style="color: rgb(52,73,94);"> <span>2、运行资源优化配置 - </span><span><strong><span>executor-memory</span></strong></span><span> </span> </p> <p style="color: rgb(52,73,94);"> <span><strong><span>参数说明</span></strong></span><span>:该参数用于</span><span><strong><span>设置每个Executor进程的内存</span></strong></span><span>。Executor内存的大小,很多时候直接决定了Spark作业的性能,而且跟常见的JVM OOM异常,也有直接的关联</span> </p> <p style="color: rgb(52,73,94);"> <span><strong><span>参数调优建议</span></strong></span><span>:</span><span><strong><span>每个Executor进程的内存设置4G~8G较为合适</span></strong></span><span>。但是这只是一个参考值,具体的设置还是得根据不同部门的资源队列来定。可以看看自己团队的资源队列的最大内存限制是多少,num-executors * executor-memory,是不能超过队列的最大内存量的。此外,如果你是跟团队里其他人共享这个资源队列,那么申请的内存量最好不要超过资源队列最大总内存的1/3~1/2,避免你自己的Spark作业占用了队列所有的资源,导致别的同事的作业无法运行。</span> </p> <p style="color: rgb(52,73,94);"> <span>3、运行资源优化配置 - </span><span><strong><span>executor-cores</span></strong></span><span> </span> </p> <p style="color: rgb(52,73,94);"> <span><strong><span>参数说明</span></strong></span><span>:该参数用于</span><span><strong><span>设置每个Executor进程的CPU core数量</span></strong></span><span>。这个参数决定了每个Executor进程并行执行task线程的能力。因为每个CPU core同一时间只能执行一个task线程,因此每个Executor进程的CPU core数量越多,越能够快速地执行完分配给自己的所有task线程</span> </p> <p style="color: rgb(52,73,94);"> <span><strong><span>参数调优建议</span></strong></span><span>:Executor的CPU core数量</span><span><strong><span>设置为2~4个较为合适</span></strong></span><span>。同样得根据不同部门的资源队列来定,可以看看自己的资源队列的最大CPU core限制是多少,再依据设置的Executor数量,来决定每个Executor进程可以分配到几个CPU core。同样建议,如果是跟他人共享这个队列,那么num-executors * executor-cores</span><span><strong><span>不要超过队列总CPU core的1/3~1/2左右比较合适</span></strong></span><span>,也是避免影响其他同事的作业运行</span> </p> <p style="color: rgb(52,73,94);"> <span>4、运行资源优化配置 - </span><span><strong><span>driver-memory</span></strong></span><span> </span> </p> <p style="color: rgb(52,73,94);"> <span><strong><span>参数说明</span></strong></span><span>:该参数用于设置Driver进程的内存</span> </p> <p style="color: rgb(52,73,94);"> <span><strong><span>参数调优建议</span></strong></span><span>:Driver的内存</span><span><strong><span>通常来说不设置,或者设置1G左右应该就够了</span></strong></span><span>。唯一需要注意的一点是,如果需要使用collect算子将RDD的数据全部拉取到Driver上进行处理(或者是用map side join操作),那么必须确保Driver的内存足够大,否则会出现OOM内存溢出的问题</span> </p> <p style="color: rgb(52,73,94);"> <span>5、运行资源优化配置 - </span><span><strong><span>spark.default.parallelism</span></strong></span><span> </span> </p> <p style="color: rgb(52,73,94);"> <span><strong><span>参数说明</span></strong></span><span>:该参数用于</span><span><strong><span>设置每个stage的默认task数量,也可以认为是分区数</span></strong></span><span>。这个参数极为重要,如果不设置可能会直接影响你的Spark作业性能</span> </p> <p style="color: rgb(52,73,94);"> <span><strong><span>参数调优建议</span></strong></span><span>:Spark作业的</span><span><strong><span>默认task数量为500~1000个较为合适</span></strong></span><span>。很多人常犯的一个错误就是不去设置这个参数,那么此时就会导致Spark自己根据底层HDFS的block数量来设置task的数量,默认是一个HDFS block对应一个task。通常来说,Spark默认设置的数量是偏少的(比如就几十个task),如果task数量偏少的话,就会导致你前面设置好的Executor的参数都前功尽弃。试想一下,无论你的Executor进程有多少个,内存和CPU有多大,但是task只有1个或者10个,那么90%的Executor进程可能根本就没有task执行,也就是白白浪费了资源!因此Spark官网建议的设置原则是,</span><span><strong><span>设置该参数为num-executors * executor-cores的2~3倍较为合适</span></strong></span><span>,比如Executor的总CPU core数量为300个,那么设置1000个task是可以的,此时可以充分地利用Spark集群的资源</span> </p> <p style="color: rgb(52,73,94);"> <span>6、运行资源优化配置 - </span><span><strong><span>spark.storage.memoryFraction</span></strong></span><span> </span> </p> <p style="color: rgb(52,73,94);"> <span><strong><span>参数说明</span></strong></span><span>:该参数用于</span><span><strong><span>设置RDD持久化数据在Executor内存中能占的比例,默认是0.6</span></strong></span><span>。也就是说,默认Executor 60%的内存,可以</span><span><strong><span>用来保存持久化的RDD数据</span></strong></span><span>。根据你选择的不同的持久化策略,如果内存不够时,可能数据就不会持久化,或者数据会写入磁盘</span> </p> <p style="color: rgb(52,73,94);"> <span><strong><span>参数调优建议</span></strong></span><span>:如果Spark作业中,有较多的RDD持久化操作,该参数的值可以适当提高一些,保证持久化的数据能够容纳在内存中。避免内存不够缓存所有的数据,导致数据只能写入磁盘中,降低了性能。但是如果Spark作业中的shuffle类操作比较多,而持久化操作比较少,那么这个参数的值适当降低一些比较合适。此外,如果发现作业由于频繁的gc导致运行缓慢(通过spark web ui可以观察到作业的gc耗时),意味着task执行用户代码的内存不够用,那么同样建议调低这个参数的值</span> </p> <p style="color: rgb(52,73,94);"> <span>7、运行资源优化配置 - </span><span><strong><span>spark.shuffle.memoryFraction</span></strong></span><span> </span> </p> <p style="color: rgb(52,73,94);"> <span><strong><span>参数说明</span></strong></span><span>:该参数用于</span><span><strong><span>设置shuffle过程中一个task拉取到上个stage的task的输出后,进行聚合操作时能够使用的Executor内存的比例,默认是0.2</span></strong></span><span>。也就是说,Executor默认只有20%的内存用来进行该操作。shuffle操作在进行聚合时,如果发现使用的内存超出了这个20%的限制,那么多余的数据就会溢写到磁盘文件中去,此时就会极大地降低性能</span> </p> <p style="color: rgb(52,73,94);"> <span><strong><span>参数调优建议</span></strong></span><span>:如果Spark作业中的RDD持久化操作较少,shuffle操作较多时,建议降低持久化操作的内存占比,提高shuffle操作的内存占比比例,避免shuffle过程中数据过多时内存不够用,必须溢写到磁盘上,降低了性能。此外,如果发现作业由于频繁的gc导致运行缓慢,意味着task执行用户代码的内存不够用,那么同样建议调低这个参数的值</span> </p> <p style="color: rgb(52,73,94);"> <span><span><strong><span style="color: inherit;">总结:</span></strong></span></span> </p> <p style="color: rgb(52,73,94);"> <span>1)num-executors:应用运行时executor的数量,推荐50-100左右比较合适</span> </p> <p style="color: rgb(52,73,94);"> <span>2)executor-memory:应用运行时executor的内存,推荐4-8G比较合适</span> </p> <p style="color: rgb(52,73,94);"> <span>3)executor-cores:应用运行时executor的CPU核数,推荐2-4个比较合适</span> </p> <p style="color: rgb(52,73,94);"> <span>4)driver-memory:应用运行时driver的内存量,主要考虑如果使用map side join或者一些类似于collect的操作,那么要相应调大内存量</span> </p> <p style="color: rgb(52,73,94);"> <span>5)spark.default.parallelism:每个stage默认的task数量,推荐参数为num-executors * executor-cores的2~3倍较为合适</span> </p> <p style="color: rgb(52,73,94);"> <span>6)spark.storage.memoryFraction:每一个executor中用于RDD缓存的内存比例,如果程序中有大量的数据缓存,可以考虑调大整个的比例,默认为60%</span> </p> <p style="color: rgb(52,73,94);"> <span>7)spark.shuffle.memoryFraction:每一个executor中用于Shuffle操作的内存比例,默认是20%,如果程序中有大量的Shuffle类算子,那么可以考虑其它的比例</span> </p>
讯享网

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/173133.html