mr调优、
最简单的调优方式
设置Combiner
Combiner在Map端提前进行了一次Reduce处理。
可减少Map Task中间输出的结果,从而减少各个Reduce Task的远程拷贝数据量,最终表现为Map Task和Reduce Task执行时间缩短。
选择合理的Writable类型
为应用程序处理的数据选择合适的Writable类型可大大提升性能。
比如处理整数类型数据时,直接采用IntWritable比先以Text类型读入在转换为整数类型要高效。
如果输出整数的大部分可用一个或两个字节保存,那么直接采用VIntWritable或者VLongWritable,它们采用了变长整型的编码方式,可以大大减少输出数据量。
作业级别调优
增加输入文件的副本数
假设集群有1个Namenode+8个Datanode节点,HDFS默认的副本数为3
那么map端读取数据的时候,在启动map task的机器上读取本地的数据为3/8,一部分数据是通过网络从其他节点拿到的
那么如果副本数设置为8会是什么情况?
相当于每个子节点上都会有一份完整的数据,map读取的时候直接从本地拿,不需要通过网络这一层了
但是在实际情况中设置副本数为8是不可行的,因为数据本身非常庞大,副本数超过5对集群的磁盘就非常有压力了,所以这项设置需要酌情处理
该配置在hdfs-side.xml的dfs.replication项中设置
1、常用combiner来减少输入到reduce的数据,防止数据倾斜
2、在数据通过远程到达reduce端进行处理的时候,可以合理的设置归并排序的次数,过多过少都不好,过多可能会写入磁盘,过少可能会增加归并排序数量
3、设置reduce的个数,来均衡原来输入到一个reduce上的数据,防止数据倾斜
spark调优,运行原理
1. RDD的持久化
cahce()
persist()
checkpoint()
2. 避免创建重复的RDD
3.尽可能复用同一个RDD
类似于多个RDD的数据有重叠或者包含的情况,应该尽量复用一个RDD,以尽可能减少RDD的数量,从而减少算子计算次数
4.尽量避免使用shuffle类算子
spark运行过程中,最消耗性能的地方就是shuffle过程(简单说,就是将分布在集群中多个节点上的同一个key拉取到同一个节点上进行操作)
shuffle过程中,各个节点上相同的key都会先写入本地磁盘文件中,然后其他节点需要通过网络传输拉取各个节点上的磁盘文件中的相同的key,而且相同key都拉取到同一个节点进行聚合操作时,还可能会因为一个节点上处理的key过多,导致内存不够存放,从而溢写到磁盘文件中。
磁盘IO和网络数据传输也是shuffle性能较差的主要原因。
尽量使用map类的非shuffle算子。
repartition(重分区)类操作:repartition、repartitionAndSortWithinPartitions、coalesce等
bykey类操作:reduceByKey、groupByKey、sortByKey等
join类操作:join、cogroup等
类如:join –>Broadcast+map (Broadcast:数据量小于1G内的RDD,每份数据放入executor副本中)
5.使用map-side预聚合的shuffle操作
因为业务需要,一定要使用shuffle操作,无法用map类算子替代,尽量使用map-side预聚合的算子。(在每个节点本地对相同的key进行一次聚合操作)
map-side预聚合之后,每个节点本地就只会有一条相同的key,因为多条相同的key都被聚合起来了。其他节点在拉取所有节点上的相同key时,就会大大减少需要拉取的数据数量,从而也就减少了磁盘IO以及网络传输开销。
通常来说,在可能的情况下,建议使用reduceByKey或者aggregateByKey算子来替代掉groupByKey算子。因为reduceByKey和aggregateByKey算子都会使用用户自定义的函数对每个节点本地的相同key进行预聚合。而groupByKey算子是不会进行预聚合的,全量的数据会在集群的各个节点之间分发和传输,性能相对来说比较差。
groupByKey中 shuffle操作中没有预聚合操作
6.使用高性能的算子
除了shuffle相关的算子有优化原则外,其他的算子也有相应的优化原则。
a . 使用mapPartitions替代map。一次函数调用会处理一个partition所有的数据,而不是一次函数调用处理一条。但是可能会出现内存溢出(OOM)问题。
b . 使用foreachPartitions提到foreach。
c . 使用filter之后进行coalesce操作。使用filter后,建议使用coalesce算子,手动减少RDD的数据量,将RDD的数据压缩到更少的partition中去。 (coalesce:RDD的分区进行重新划分,repartition只是coalesce接口中shuffle为true的简易实现)
d . 使用repartitionAndSortWithinPartitions替代repartition与sort类操作。如果需要在repartition重分区后,还有进行排序,建议使用repartitionAndSortWithinPartitions。因为该算子可以一边进行重分区的shuffle操作,一边进行排序(同时进行)。
7.广播大变量
在开发中,遇到需要在算子函数中使用外部变量的场景(如配置文件)(尤其是大变量,比如100M以上的大集合)。此时就应该使用Spark的广播功能来提升性能
在算子函数中使用到外部变量时,默认情况下,spark会将变量复制多个副本,通过网络传递到task中,此时每个task都有一个变量副本。若使用的外部变量比较大,建议使用Spark的广播功能,对该变量进行广播。广播后的变量会保证每个executor的内存中,只驻留一份变量副本,而executor中的task执行时共享该executor中的那份变量副本。从而大大减少变量副本的数量,减少网络传输的性能开销,并减少对executor内存的占用开销。
8.使用kryo优化序列化性能
JAVA序列化:是指把JAVA对象转换位字节序列的过程;而JAVA反序列化是指把字节序列恢复为JAVA对象的过程。
当两个进程进行远程通信时,可以相互发送各种类型的数据,包括文本、图片、音频、视频等,而这些数据都会以二进制序列的形式在网络上传送。那么当两个JAVA进程进行通信时,要实现