王家林谈Spark性能优化第四季!
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了王家林谈Spark性能优化第四季!相关的知识,希望对你有一定的参考价值。
内容:
1、序列化;
2、JVM性能调优;
==========Spark性能调优之序列化============
1、之所以进行序列化,最重要的原因是:内存空间有限(减少GC的压力,最大化避免Full GC的产生,一旦产生Full GC,则整个Task则处于停止状态)、减少磁盘IO的压力、减少网络IO的压力;
2、什么时候会必要的产生序列化和反序列化呢?发生磁盘IO和网络通信的时候会发生序列化和反序列化,更为重要的考虑序列化和反序列化的时候有另外两种情况:
1)Persist(Checkpoint)的时候必须考虑序列化和反序列化,例如说cache到内存的时候只能使用JVM分配的60%的内存空间,此时好的序列化机制就至关重要了;
2)编程的时候!使用算子的函数操作如果传入了外部就必须序列化和反序列化;
例如:
val person = new Person()
rdd.map(item=>person.add(item))
这样写可以吗?(可以用来面试别人的!!!)
代码这样写是绝对不可以的,因为代码是在Driver中的,代码必须传给Executor,必须用序列化和反序列化的
使用Kryo的写法,正确的是这样的:
conf.set(spark.serializer
org.apache.spark.serializer.KryoSerializer )
conf.registryKryoClass(Array(classOf[Person]))
val person = new Person()
rdd.map(item=>person.add(item))
其实从效率而言,用BroadCast效率更高,此处知识说序列化和反序列化!!!
3、强烈建议使用Kryo序列化器进行序列化和反序列化,Spark默认情况下不是使用的Kryo,而是Java自带的序列化器ObjectInputStream和ObjectOutputSteam(主要是考虑了方便性或者通用性),在默认情况下如果自定义了RDD中数据元素的类型则必须实现Serializable,当然您也可以实现自己的序列化接口Externalizable来实现更加高效的Java序列化算法,但是采用默认的ObjectInputStream和ObjectOutputSteam 会导致序列化后的数据占用大量的内存或者磁盘或者大量的消耗网络,并且在序列化和反序列化的时候比较消耗CPU;
4、强烈采用Kryo序列化机制,Spark下使用Kryo序列化机制,会比Java默认的序列化机制更加节省空间(节省近10倍的空间)以及更小的消耗CPU,个人强烈建议大家在一切情况下尽可能使用Kryo序列化机制 ;
5、使用Kryo的两种方式:
1)在spark-defaults.conf中配置;
2)在程序的SparkConf中配置;
conf.set(spark.serializer
org.apache.spark.serializer.KryoSerializer )
使用Kryo可以更加快速、更低存储空间占用量以及更高性能的角度方式来进行序列化;
6、Spark中Scala常用的类型自动的通过AllScalaRegisty注册给了Kryo进行序列化管理;
7、如果是自定义的类型必须注册给序列化器,例如第2点中的;
8、Kryo在序列化的时候,缓存空间默认大小是2MB,可以根据具体的业务模型调整该大小,具体方式:设置spark.kyroserializer.buffer为10MB,因为有时候可能有;
9、在使用Kryo强烈建议注册时写完成的包名和类名,否则的话每次序列化的时候都会保存一份整个包名和类名的完整信息,这就会不必要的消耗内存空间;
==========Spark JVM性能调优 ============
1、好消息是Spark的钨丝计划是用来专门解决JVM性能问题的,不好的消息至少在Spark2.0以前钨丝计划功能不稳定且不完善,且只能在特定的情况下发生作用,也就是说包括Spark1.6.0在内的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:+ PrintGCDateTimeSatmps
2)sparkUI
5、Spark默认情况下使用60%的空间来进行cache缓存RDD的内容,也就是说Task在执行的时候,只能使用剩下的40% 的空间,如果空间不够用,就会触发(频繁的)GC
可以设置spark.memory.fraction参数来调整空间的使用,例如降低Cache的空间,让Task使用更多的空间来创建对象和完成计算;
再一次,强烈建议进行从RDD进行cache的时候使用Kryo进行序列化,从而给Task可以分配更大的空间来顺利完成计算(避免频繁的GC);
6、因为在老年带空间满的时候会发生FULL GC操作,而老年带空间中基本都是活的比较久的对象(经历数次GC依旧存在),此时会停下所有的程序线程,进入FULL GC,对OLD区中的对象进行整理,严重影响性能,此时可以考虑:
1)设置spark.memory.fraction参数来进行调整空间的使用来给年轻代更多的空间用于存放短时间存活的对象;
2)-Xmn调整Eden区域:对RDD中操作的对象和数据进行大小评估,如果在HDFS上解压后一般体积可能会变成原有体积的3倍左右,根据数据的大小来设置Eden,如果有10个Task,每个Task处理的HDFS上的数据是128MB,则需要设置-Xmn为10*128*3*4/3的大小;
3)-XX:SupervisorRatio;
4)-XX:NewRatio;
SupervisorRatio、NewRatio 正常情况下不用随便调,前提是对JVM非常了解的前提下
但是数据级别到PB级别之后,就完全不是这么一回事了,就要去研究JVM了!!!
王家林老师名片:
中国Spark第一人
新浪微博:http://weibo.com/ilovepains
微信公众号:DT_Spark
博客:http://blog.sina.com.cn/ilovepains
手机:18610086859
QQ:1740415547
本文出自 “一枝花傲寒” 博客,谢绝转载!
以上是关于王家林谈Spark性能优化第四季!的主要内容,如果未能解决你的问题,请参考以下文章
王家林谈Spark性能优化第十季之全球独家揭秘Spark统一内存管理!
王家林谈Spark性能优化第九季之Spark Tungsten内存使用彻底解密