Spark性能优化第四季-序列化

一:Spark性能调优之序列化
1、之所以进行序列化,最重要的原因是内存空间有限(减少GC的压力,最大化避免Full GC的产生,因为一旦产生Full GC,则整个Task处于停止状态!)、减少磁盘IO的压力、减少网络IO的压力;
2、什么时候会必要的产生序列化和反序列化?发送磁盘IO和网络通信的时候会序列化和反序列化,更为重要的考虑序列化和反序列化的时候有另外两种情况:1)Persist(Checkpoint)的时候必须考虑序列化和反序列化,例如cache到内存时默认只能使用JVM分配的60%内存空间,此时,好的序列化机制就至关重要了。2)编程开发,使用算子的函数操作如果传入了外部数据(因为外部数据有可能涉及到磁盘读写、或者网络IO)就必须序列化和反序列化,例如:
val person = new Person
rdd.map(item => person.add(item))。解析:程序在Driver上,但是rdd的执行在Executor上,这样,程序在执行时会报持久化异常,需要对对象进行序列化。
解决方法:将Person注册到Kyro或者继承Serializable接口
conf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
conf.registryKryoClass(Array(classOf[Person]))
2、强烈建议使用Kryo序列化器进行序列化和反序列化;Spark默认情况下不是使用的Kryo,而是Java自带的序列化器ObjectInputStream和ObjectOutputStream(主要是考虑了方便性或者通用性),在默认情况下如果自定义了RDD中数据元素的类型则必须实现Serializable接口,当然,也可以实现自己的序列化接口Externalizable来实现更加高效的Java序列化算法;采用默认ObjectInputStream和ObjectOutputStream会导致序列化后的数据占用大量的内存或者磁盘及大量消耗网络,且在序列化和反序列化的时候比较消耗CPU;
3、强烈推荐大家采用Kryo序列化机制,Spark下使用Kryo序列化机制会比Java默认的序列化机制更加节省空间(节省10倍空间左右)以及更少的CPU资源消耗;建议在多数情况下尽可能使用Kryo序列化机制;
4、使用Kryo的两种方式:
1)在spark-defaults.conf中配置;
2)在程序的SparkConf中配置,conf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
5、Spark中Scala常用的类型自动的通过AllScalaRegistry注册给了Kryo进行序列化管理;
7、如果是自定义的类型必须注册给序列化器,例如:
conf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
conf.registryKryoClass(Array(classOf[Person]))
val person = new Person
rdd.map(item => person.add(item))
在使用序列化时,可能存在序列化对象本身很大的问题。
8、在使用序列化时,可能存在序列化对象本身很大的问题。Kryo在序列化的时候缓存空间默认大小为2MB,可以通过spark.kryoserializer.buffer来调整大小。
9、在使用Kryo强烈建议注册时写完整的包名和类名,否则的话每次序列化的时候都会保存一份整个包名和类名的完整信息,这就会消耗不必要的内存空间;


二:Spark JVM性能调优
1、Spark钨丝计划是用来专门解决JVM性能问题的,但是在Spark2.0以前,钨丝计划功能不稳定且不完善,只能在特定的情况下发生作用(也就是说在包括Spark1.6.X及以前的spark版本在多数情况下没有使用钨丝计划的功能,所以此时必须关注JVM性能调优);
2、JVM性能调优的关键是调优GC。为什么GC如此重要?主要是因为Spark热衷于RDD的持久化!!GC本身的性能开销是和数据量成正比的。
3、初步考虑是尽量多的使用array和String,并且在序列化机制方面尽可能的采用Kryo,让每个partition都成为字节数组;
4、监视GC的基本方式有两种:
1)配置 spark.executor.extraJavaOptions= -verbose:gc -XX:+PrintGCDetails -XX:+ PrintGCDateTimeStamps
2)SparkUI
5、Spark在默认情况下使用60%的内存空间来进行Cache缓存RDD的内容,也就是说Task在执行的时候只能使用剩下40%的内存空间。如果空间不够用就会触发(频繁的)GC


6、可以设置spark.memory.fraction参数来进行调整空间的使用,例如降低Cache的空间,让Task使用更多的空间来创建对象和完成计算;
再一次强烈建议进行RDD的Cache操作时使用Kryo序列化机制,从而给Task可以分配更大的空间来顺利完成计算(避免频繁的GC)。
6、因为在老年代空间满的时候会发送Full GC操作,而老年代空间中基本都是活的比较久的对象(经历数次GC依旧存在),此时会停止所有程序线程,进行Full GC,对Old区中的对象进行整理,这严重影响了性能;
解决方法:(默认参数谨慎调整,需要建立在对JVM有很高的熟知程度)
1)设置spark.memory.fraction参数来进行调整空间的使用来给年轻代更多的空间用于存放短时间的存活对象
2)-Xmn 调整Eden区域。RDD中操作的对象和数据进行大小评估。如果在HDFS上上解压后一般为原有体积的3倍左右,根据数据的大小来设计Eden;例如:如果有10个Task,每个Task处理的HDFS上的数据是128M,则需要设置 -Xmn为 10*138*3*4/3的大小;
3)-XX:SupervisorRatio
4) -XX:NewRatio
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值