1.分配资源,并行度,rdd架构与缓存
2.shuffle调优
3.spark算子调优
4.jvm调优,广播大变量。。。
1:分配更多的资源
分配那些资源?
excutor ,cpu per ececuter,memory per executer
在哪里分配资源?
在提交spark提交作业的脚步中,提交参数
调节到多大算是最大呢?
为什么调节了资源之后,性能可以提升?
2:调节并行度
并行度设置成CPU数据的2-3倍,通过新建spark上下文设置参数
如何设置一个Spark Application的并行度?
spark.default.parallelism
SparkConf conf = new SparkConf()
.set("spark.default.parallelism", "500")
3:重构RDD已经RDD持久化
3.1.rdd架构重构与优化
尽量去复用rdd,差不多的rdd,可以抽取成一个共同的rdd,供后面的rdd计算时,反复的使用
3.2.公共的rdd 一定要持久化
3.3.持久化是可以序列化的 (正常数据持久化,可能导致内存过大)
内存不够时,首先可以序列化后存在内存中-获取数据时需要反序列化
OOM 内存溢出
优先 内存
内存+序列化
其次 考虑 内存+磁盘的方式
最次的 是 内存+磁盘 +序列化
3.4.为了数据的可靠性 可以是用双副本机制。
把持久化的每个副本单元 存在其他节点,一个副本丢失,不需要重新计算
资源很充足
4:广播大变量
5.实际羡慕中使用kryo序列化
SparkConf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
首先第一步,在SparkConf中设置一个属性,spark.serializer,org.apache.spark.serializer.KryoSerializer类;
Kryo之所以没有被作为默认的序列化类库的原因,就要出现了:主要是因为Kryo要求,如果要达到它的最佳性能的话,那么就一定要注册你自定义的类(比如,你的算子函数中使用到了外部自定义类型的对象变量,这时,就要求必须注册你的类,否则Kryo达不到最佳性能)。
第二步,注册你使用到的,需要通过Kryo序列化的,一些自定义类,SparkConf.registerKryoClasses()
项目中的使用:
.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
.registerKryoClasses(new Class[]{CategorySortKey.class})
6.在实际项目中使用fastutil优化数据格式
7.在实际项目中调节数据本地化等待时长.
数据本地化级别
PROCESS_LOCAL:进程本地化,代码和数据在同一个进程中,也就是在同一个executor中;计算数据的task由executor执行,数据在executor的BlockManager中;性能最好
NODE_LOCAL:节点本地化,代码和数据在同一个节点中;比如说,数据作为一个HDFS block块,就在节点上,而task在节点上某个executor中运行;或者是,数据和task在一个节点上的不同executor中;数据需要在进程间进行传输
NO_PREF:对于task来说,数据从哪里获取都一样,没有好坏之分
RACK_LOCAL:机架本地化,数据和task在一个机架的两个节点上;数据需要通过网络在节点之间进行传输
ANY:数据和task可能在集群中的任何地方,而且不在一个机架中,性能最差
spark.locality.wait,默认是3s
8.JVM调优之原理概述以及降低cache操作的内存占比
spark.storage.memoryFraction,0.6 -> 0.5 -> 0.4 -> 0.2
9.用户访问session分析JVM调优之调节executor堆外内存与连接等待时长
调节executor堆外内存
--conf spark.yarn.executor.memoryOverhead=2048
spark-submit脚本里面,去用--conf的方式,去添加配置;一定要注意
连接等待时长
--conf spark.core.connection.ack.wait.timeout=300
10.Shuffle调优之合并map端输出文件
new SparkConf().set("spark.shuffle.consolidateFiles", "true")
开启shuffle map端输出文件合并的机制;默认情况下,是不开启的,就是会发生如上所述的大量map端输出文件的操作,严重影响性能。
11.Shuffle调优之调节map端内存缓冲与reduce端内存占比
调优:
调节map task内存缓冲:spark.shuffle.file.buffer,默认32k(spark 1.3.x不是这个参数,后面还有一个后缀,kb;spark 1.5.x以后,变了,就是现在这个参数)
调节reduce端聚合内存占比:spark.shuffle.memoryFraction,0.2
在实际生产环境中,我们在什么时候来调节两个参数?
看Spark UI,如果你的公司是决定采用standalone模式,那么狠简单,你的spark跑起来,会显示一个Spark UI的地址,4040的端口,进去看,依次点击进去,可以看到,你的每个stage的详情,有哪些executor,有哪些task,每个task的shuffle write和shuffle read的量,shuffle的磁盘和内存,读写的数据量;如果是用的yarn模式来提交,课程最前面,从yarn的界面进去,点击对应的application,进入Spark UI,查看详情。
如果发现shuffle 磁盘的write和read,很大。这个时候,就意味着最好调节一些shuffle的参数。进行调优。首先当然是考虑开启map端输出文件合并机制。
调节上面说的那两个参数。调节的时候的原则。spark.shuffle.file.buffer,每次扩大一倍,然后看看效果,64,128;spark.shuffle.memoryFraction,每次提高0.1,看看效果。
不能调节的太大,太大了以后过犹不及,因为内存资源是有限的,你这里调节的太大了,其他环节的内存使用就会有问题了。
调节了以后,效果?map task内存缓冲变大了,减少spill到磁盘文件的次数;reduce端聚合内存变大了,减少spill到磁盘的次数,而且减少了后面聚合读取磁盘文件的数量。
12.Shuffle调优之HashShuffleManager与SortShuffleManager
spark.shuffle.manager:hash、sort、tungsten-sort(自己实现内存管理)
spark.shuffle.sort.bypassMergeThreshold:200
大家理解的更加深入了。hash、sort、tungsten-sort。如何来选择?
1、需不需要数据默认就让spark给你进行排序?就好像mapreduce,默认就是有按照key的排序。如果不需要的话,其实还是建议搭建就使用最基本的HashShuffleManager,因为最开始就是考虑的是不排序,换取高性能;
2、什么时候需要用sort shuffle manager?如果你需要你的那些数据按key排序了,那么就选择这种吧,而且要注意,reduce task的数量应该是超过200的,这样sort、merge(多个文件合并成一个)的机制,才能生效把。但是这里要注意,你一定要自己考量一下,有没有必要在shuffle的过程中,就做这个事情,毕竟对性能是有影响的。
3、如果你不需要排序,而且你希望你的每个task输出的文件最终是会合并成一份的,你自己认为可以减少性能开销;可以去调节bypassMergeThreshold这个阈值,比如你的reduce task数量是500,默认阈值是200,所以默认还是会进行sort和直接merge的;可以将阈值调节成550,不会进行sort,按照hash的做法,每个reduce task创建一份输出文件,最后合并成一份文件。(一定要提醒大家,这个参数,其实我们通常不会在生产环境里去使用,也没有经过验证说,这样的方式,到底有多少性能的提升)
4、如果你想选用sort based shuffle manager,而且你们公司的spark版本比较高,是1.5.x版本的,那么可以考虑去尝试使用tungsten-sort shuffle manager。看看性能的提升与稳定性怎么样。
总结:
1、在生产环境中,不建议大家贸然使用第三点和第四点:
2、如果你不想要你的数据在shuffle时排序,那么就自己设置一下,用hash shuffle manager。
3、如果你的确是需要你的数据在shuffle时进行排序的,那么就默认不用动,默认就是sort shuffle manager;或者是什么?如果你压根儿不care是否排序这个事儿,那么就默认让他就是sort的。调节一些其他的参数(consolidation机制)。(80%,都是用这种)
spark.shuffle.manager:hash、sort、tungsten-sort
new SparkConf().set("spark.shuffle.manager", "hash")
new SparkConf().set("spark.shuffle.manager", "tungsten-sort")
// 默认就是,new SparkConf().set("spark.shuffle.manager", "sort")
new SparkConf().set("spark.shuffle.sort.bypassMergeThreshold", "550")
13.算子调优之filter过后使用coalesce减少分区数量
14.算子调优之使用foreachPartition优化写数据库性能
正式生产环境中一般都是是foreachPartition进行数据的的写操作,一次性操作一个分片的数据
但是一个分片的数据量特别大时,可能会导致OOM
15.算子调优之使用repartition解决Spark SQL低并行度的性能问题
spark SQL 并不能设置并行度,通过repartition进行重新分区
通常情况下 设置 spark.default.parallelism 设置并行度
16.算子调优之reduceByKey本地聚合 的特性