Spark性能调优(五)---其他(广播大变量、Kryo序列化等)性能调优策略

1.广播大变量

       被大量task使用的变量,使用广播。被广播的变量,在Driver中存在一个初始副本。这样,就不需要每个task都拥有一个变量的副本,节省网络传输的资源和内存的资源;每一个BlockManager有一个变量的副本,BlockManager中没有变量的时候,可以去Driver中获取,也可以从距离最近的其他BlockManager中获取。

       设置广播变量:sc.broadcast();传入变量,如:

Broadcast>>> dateHourExtractMapBroadcast = sc.broadcast(dateHourExtractMap);
     
       获取广播变量:广播变量.getValue(),如:

Map>> dateHourExtractMap = dateHourExtractMapBroadcast.getValue();
 

2.使用Kryo序列化机制

2.1默认的序列化机制

        默认情况下,Spark内部使用的是Java的序列化机制,ObjectOutputStream/ObjectInputStream,对象输入输出流机制,来进行序列化。

       这种默认的序列化机制的好处在于,处理起来比较方便,也不需要我们手动做什么,只是在算子中使用的变量,必须实现Serializable接口,可序列化即可。缺点在于,默认的序列化机制效率不高,序列化的速度慢,序列化之后的数据,占用的空间相对还是比较大。。

2.2Kryo序列化机制

       Spark支持使用Kryo序列化机制,比默认的序列化机制速度快,占用空间小(官方说明,仅为默认序列化机制的1/10)。所以,用Kryo序列化机制进行序列化后,网络传输更快,在集群中占用内存资源更少。

2.3Kryo序列化机制生效的几个地方

1.算子函数中使用到的外部变量
2.持久化RDD是进行序列化,比如StorageLevel.MEMORY_ONLY_SER
3.shuffle
4.算子函数中使用到的外部变量,使用Kryo之后,优化网络传输的性能,可以优化集群中内存的占用和消耗
5.持久化RDD,优化内存占用和消耗;持久化RDD占用的内存越少,task执行的时候,创建的对象,就不至于频繁地沾满内存,发生GC
6.shuffle,可以优化网络传输的性能

2.4Kryo的使用

       在SparkConf中设置属性,spark.serializer,org.apache.spark.serializer.KryoSerializer类
注册使用到的自定义的类
       
       Kryo没有被设置成默认的序列化机制,是因为要达到Kryo的最佳性能,必须要注册自定义的类。比如,算子函数中使用到了外部自定义类型的对象变量,必须注册使用到的类,否则Kryo达不到最佳性能。

项目中的使用:

.set("spark.serializer","org.apache.spark.serializer.KryoSerializer").registerKryoClasses(new Class[]{Student.class})

3.使用fastutil优化数据格式

3.1fastutil介绍

       fastutil是扩展了Java标准集合框架(Map、List、Set;HashMap、ArrayList、HashSet)的类库,提供了特殊类型的map、set、list和queue;

      fastutil能够提供更小内存的占用,更快的存取速度;使用fastutil提供的集合类来替代自己平时使用的JDK原生的Map、List、Set,可以减少内存的占用,并且在进行集合的遍历、根据索引(或者key)获取元素的值和设置元素的值的时候,提供更快的存取速度;

      fastutil也提供了64位的array、set和list,以及高性能快速的,以及使用的IO类,来处理二进制和文本类型的文件;

      fastutil的每一种集合类型,都实现了对应的Java中的标准接口(比如fastutil的map,实现了Java的Map接口),因此可以直接放入已有系统的任何代码中;

      fastutil还提供了一些JDK标准类库中没有的额外的功能(比如双向迭代器);

      fastutil除了对象和原始类型为元素的集合,也提供引用类型的支持,但是对引用类型是使用等于号(=)进行比较,而不是equals()方法;

      fastutil尽量提供在任何场景下都是速度最快的集合类库;

3.2Spark中应用fastutil的场景

      1.如果算子函数使用了外部变量,首先可以使用Broadcast广播变量优化;其次可以使用Kryo序列化类库,提升序列化性能和效率;最后,如果外部变量是某种比较大的集合,可以考虑使用fastutil改写外部变量。首先从源头上减少内存的占用,通过广播变量进一步减少内存占用,再通过Kryo序列化类库进一步减少内存占用

      2.在算子函数里,也就是task要执行的计算逻辑里,如果要创建比较大的Map、List集合,可能会占用较大的内存空间,而且可能涉及到消耗性能的遍历、存取等集合操作。这种情况下,可以考虑将这些集合类型使用fastutil类库重写,使用了fastutil集合类以后,就可以在一定程度上,减少task创建出来的集合类型的内存占用。避免executor内存频繁占满,频繁唤醒GC,导致性能下降

3.3关于fastutil调优的说明

        fastutil调优效果可能并没有我们想象中的好。举个例子,一个spark作业,在经过资源、并行度、RDD序列化的调优之后,30分钟可以运行完;那么再使用broadcast、Kryo和fastutil进行优化,可能29分钟运行完。所以效果可能不会像我们想象的那么好。

3.4.fastutil的使用

3.4.1在pom.xml文件中引入fastutil的依赖


    fastutil
    fastutil
    5.0.9

3.4.2将Java中的类改成fastutil对应的类

      比如,List 改为 InList;基本都是类似于IntList的格式,前缀都是集合的元素类型;

      Map比较特殊,如Int2IntMap,代表了key-value映射的元素类型。

      除此之外,还支持object、reference等。

4.调节数据本地化等待时长

4.1概述

PROCESS_LOCAL:进程本地化,代码和数据在同一个进程executor中,计算数据的task有executor执行,数据和executor在同一个BlockManager中;性能最好

NODE_LOCAL:节点本地化,代码和数据在同一个节点中,但是计算数据的task和数据不在同一个executor中,数据需要在进程之间进行传输

NO_PREF:对于task来说,数据从哪里获取,对性能没有影响

RACK_LOCAL:机架本地化,数据和task在同一个机架的两个节点上;数据需要在节点之间通过网络进行传输

ANY:数据和task可能在集群中的任何地方,也可能不在同一个机架上,性能最差

4.2什么时候调节这个参数

       观察日志,spark作业的运行日志。在测试的时候,先用client模式,在本地就可以直接看到比较全的日志。日志里面会显示,starting task...,PROCESS_LOCAL,NODE_LOCAL。观察大部分task的数据本地化级别。

       如果大多数都是PEOCESS_LOCAL,则不用调节;

       如果大部分级别都是NODE_LOCAL,ANY,那么最好调节一下数据本地化的等待时长;调节完,再次运行,观察日志,反复调节,观察大部分的task的本地化级别有没有提升,以及整个spark作业的 运行时间是否缩短;

      切忌,本地化级别提升了,但是因为大量的等待时长,导致整个spark作业的执行时间增加,这样就得不偿失了。

4.3怎么调节

spark.locality.wait,默认是3s。

默认情况下,下面3个等待时长,都是和spark.locality.wait相同的,都是3s:

spark.locality.wait.process

spark.locality.wait.node

spark.locality.wait.rack

设置代码:

new SparkConf()

.set("spark.locality.wait","6")

 

 

 

点赞 1
————————————————
版权声明:本文为CSDN博主「Johnson8702」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Johnson8702/article/details/86705817

你可能感兴趣的:(BigData大数据相关)