Spark 优化

Spark 优化

一、常规性能调优:

1. 最优资源配置:

  • 在一定范围内,增加资源分配,合理的增加服务器数量、增加内存。
  1. Driver内存:增加Driver内存的影响不是特别大,只有少部分数据会在Driver端执行。
  2. Executor数量:可以增大Task的并行度。
  3. Executor内存:
    <1>可以缓存更多的数据,减少数据写磁盘的次数,从而减少网络IO。
    <2>可以为Shuflle提供更多的存储空间来存放Reduce拉取的数据,减少数据写磁盘后拉取的网络IO。
    <3>可以为task的执行提供更多内存,较少task执行过程中创建过多对象导致频繁的GC,提高整体性能。
  4. Executor的CPU核数:可以增加Task的并行度。

2.RDD的优化:

  1. RDD复用:对RDD进行操作时要避免使用相同的算子和计算逻辑对RDD进行重复的计算。
  2. RDD持久化:必须对多次使用的RDD进行持久化,将持久化的的数据存储到内存或磁盘中,避免重复计算造成的资源浪费。
    说明:RDD的持久化时可以序列化的,并且内存充足时可以使用副本机制。
  3. RDD尽早filter:减少非必要数据对内存的占用。

3.并行度优化:

  • 尽可能的让并行度与资源相匹配,Task数量应该设置为Spark作业总core数量的2到3倍。

4.广播变量:

  • 广播变量在每个Executor保存一个副本,此Executor的所有task共用此广播变量,这让变量产生的副本数量大大减少。在初始阶段,广播变量只在Driver中有一份副本。Task在运行的时候想要使用广播变量中的数据,此时首先会在自己本地的Executor对应的BlockManager中尝试获取变量,如果本地没有BlockManager就会从Driver或者其他节点的BlockManager上远程拉取变量的复本,并由本地的BlockManager进行管理;之后此Executor的所有task都会直接从本地的BlockManager中获取变量。

5.Kryo序列化:

  • Spark默认情况下使用Java序列化机制,序列化速度慢并且序列化后的数据所占用大量空间。而Kyro序列化方式比Java序列化机制性能提高10倍左右,但是由于它不支持所有对象的序列化,同时Kryo需要用户在使用前注册需要序列化的类型,不够方便,所以并没有默认使用Kryo序列化方式。

6.调节本地化等待时长:

  • Spark希望task能够运行在它要计算的数据所在的节点,从而避免网络传输。如果当前节点资源已经用尽,Spark会等待一段时间,默认3S,如果等待指定时间后仍然无法在指定节点运行,那么会自动降级尝试将task分配到比较差的本地化级别所对应的节点上,如果当前级别仍然不行,那么继续降级,我们可以增加等待时常,设置为6S,不能太长,否则会增加任务的运行时间。

二、算子优化:

1.MapPartitions:

  • 普通的map方法针对RDD分区中的每个数据,而mapPartitions针对RDD中的每个分区。在值对数据进行一次操作时,使用map与mapPartitions效率上没有太大区别,而需要对数据进行多次操作时,需要将mapPartitions中的迭代器转换成集合,然后再掉用map方法即可,此时mapPartitions算子的效率比map算子效率高,因为mapPartitions中的数据调用的时map方法,比算子的执行效率高。
  • 缺点:当数据量非常大的时候使用mapPartitions可能会导致内存溢出,因此再使用mapPartitions之前需要对RDD中的数据进行估算。
  • 开发中的使用:在项目开发过程中需要连接第三方数据库,此时通常使用mapPartitions,每个分区获取一个连接,而map算子会每个数据获取一个连接。

2.ForeachPartition优化数据库操作:

  • 在生产环境中通常使用foreachPartition算子来完成数据库的写入,针对每个分区获取一个连接,而foreach会遍历RDD中的所有数据,每条数据会获取一个连接,由于所有的连接都是不可以序列化的,不能放在Driver端,所以只能在Executor端获取,并且我们选择分区创建连接。

3.Filter与coalesce的配合使用:

  • 当使用filter算子进行数据过滤后会减少数据量,之后可以使用coalesce改变并行度(减小分区数),从而提高分区的资源利用效率。

4.ReduceByKey预聚合:

  • Map端会先对本地的数据进行combine操作,然后将数据写入给下个stage的每个task创建的文件中,减少map端输出的数据量,从而减少网络IO,同时减少Reduce拉取数据时的IO,Reduce端加载到内存中的数据、聚合的数据都会相应减少。

三、Shuffle 优化:

1.调节map端缓冲区大小:

  • 在Spark任务运行过程中,如果shuffle的map端处理的数据量比较大,而map端缓冲的大小是固定的,可能会出现map端缓冲数据频繁spill溢写到磁盘文件中的情况,使得性能非常低下,通过调节map端缓冲的大小可以避免频繁的磁盘IO操作,进而提升Spark任务的整体性能。默认32KB,可以增加到64K。

2.调节reduce端拉取数据缓冲区大小:

  • Reduce端buffer缓存区的大小决定了每次能够拉去数据量的大小,如果该值太小会频繁的到磁盘中拉取数据,效率很低,可以对应的调大reduce端Buffer的大小,减少拉取次数,默认为48MB。

3.调节reduce端拉取数据重试次数:

  • 任务拉取失败时会进行重试,增加一定的重试次数,避免整个任务失败,比任务重启再执行效率更高,默认是3。

4.调节reduce端拉取数据等待间隔:

  • 增大拉取次数的同时,相应的增大拉取数据的等待时间,不能短时间内频繁的拉取数据。

5.调节SortShuffle排序操作阈值:

  • 对于SortShuffleManager,如果ReduceTask的数量小于某一阈值则溢写过程中不会进行排序操作,而是直接按照未经优化的HashShuffleManager的方式去写数据,但是最后会将每个task产生的所有临时磁盘文件都合并成一个文件,并会创建单独的索引文件。并且增大该阈值可以增加能够使用ByPassShuffle数量。

    ByPassShuffle的使用条件:
    1.Reduce的数量小于200,因为ByPassShuffle有多少个Reduce就会有多少个溢写文件,为了防止中间过程产生大量小文件。
    2.不能是预聚合算子,因为预聚合算子再map端会进行排序,才能将相同key的数据放在一起。
    如何查看算子是否是预聚合算子:每个算子都定义了一个mapSideCombiner属性,用来指定是否需要预聚合,默认是True。

四、JVM 优化:

1.降低cache操作的内存占比:

  • Spark将堆内存被分为两块,Storage存储区和Execution计算区,将Cache放在Storage中,由于早期Spark是静态内存管理,Storage占系统内存的60%,Execution占系统内存的20%,并且两者完全独立,现在为 统一内存管理机制,各占50%。

2.调节Executor堆外内存:

  • 如果Spark作业处理的数据量非常大,达到几亿的数据量,此时运行Spark作业会时不时地报错,可能是Executor的堆外内存不太够用,导致Executor在运行的过程中内存溢出。堆外内存默认不开启,需要手动开启,且Cache时需要手动指定。Spark中交给JVM管理的内存为堆内内存,由Spark交给操作系统管理的为堆外内存。

3.调节连接等待时长:

  • Spark在运行过程中从其他节点获取数据时,会因为GC清理而无法建立网络连接导致的连接超时,此时我们可以增大连接等大时长。
posted @ 2021-05-31 17:02  yuexiuping  阅读(87)  评论(0编辑  收藏  举报