Spark调优
-
资源调优合理设置Driver和Executor的个数以及他们的核数和内存大小
-
RDD优化
-
RDD复用,对RDD进行算子时,要避免相同的算子和计算逻辑下对RDD进行重复计算
-
RDD持久化,当多次对同一个RDD执行算子操作时,每一次都会对这个RDD以之前的夫RDD
重新计算,要避免这种情况,要对多次使用的RDD进行持久化
-
-
并行度调节
-
理想的并行度设置,是让并行度和资源相匹配,就是在资源允许的前提下,并行度要设置的尽可能大,达到可以充分利用集群资源。合理的设置并行度,可以提升整个Spark作业的性能和运行速度,官方推荐,task数量应该设置为Spark作业总CPU Core数量的2~3倍
val conf = new SparkConf().set("spark.default.parallelism","500")
-
-
广播大变量
-
默认情况下,task中的算子中如果使用了外部的变量。每个task都会获取一份变量的副本,
这造成了内存的极大消耗,一方面如果后续对RDD进行持久化,可能就无法将RDD数据存入内存,只能写入磁盘,磁盘IO将会严重消耗性能,另一方面task在创建对象的时候,堆内存无法存放新创建的对象,这会导致频繁的GC。
-
-
使用Kryo序列化
- 默认情况下,Spark使用的是Java的序列化机制,优点是Java的序列化机制使用方便,不需要额外的配置,在算子中使用的变量实现Serializable接口即可,缺点是Java序列化机制的效率不高,序列化速度慢并且序列化后的数据所占用的空间依然很大。
- Kryo序列化机制比Java序列化性能提高了10倍左右,缺点是kryo不支持所有对象的序列化,同时Kryo需要用户在使用前注意需要序列化的类型,不够方便,但是从Spark2.0.0开始简单类型、简单类型数组、字符串类型的shuffing RDDs,已经默认使用Kryo序列化方式了
-
使用高性能算子
- 比如使用mapPartition替代map,map算子对RDD中的每一个元素进行操作,而mapPartition算子对RDD中每一个分区进行操作,优点是速度快效率高,缺点是在内存不足时会产生OOM
- 使用foreachPartiton替代foreach,如果使用foreach算子完成对数据库的操作,由于foreach算子是遍历RDD的每条数据,因此每条数据都会建立一个数据库连接,这是对资源的极大浪费,因此对于写数据库操作,我们应当使用foreachPartition。
- 使用filter与coalesce配合使用:在Spark任务中我们经常会使用filter算子完成对RDD中数据的过滤,在任务初始阶段从各个分区加载到的数据量是相近的,但是一旦经过filter过滤后,每个分区的数据量有可能会存在较大差异,对数据量较大的分区需要需要增加分区的数量,对数据量较小的分区,需要缩减分区的数量。
- 使用repartition解决Spark SQL低并行度的问题
- 使用reduceByKey 预聚合
-
Shuffle调优
-
调节map端缓冲区的大小
在Spark任务运行的过程中,如果shuffle的map端处理的数据量比较大,但是map端缓冲区的大小时固定的,可能会出现map端缓冲数据频繁的spill溢写到磁盘文件的情况。通过调节map端缓冲区的大小,可以避免频繁的磁盘IO操作,从而提升Spark任务的整体性能
val conf = new SparkConf().set(“spark.shuffle.file.buffer”,"64")
-
调节reduce端拉取数据缓冲区大小
在Spark Shuffle过程中,shuffle reduce task 的buffer 缓冲区大小决定了reduce task每次能够缓冲的数据量,也就是每次能够拉取的数据量,如果内存资源较为充足,适当增加拉取数据缓冲区的大小,可以减少拉取数据的次数,也就可以减少网络传输的次数,进而提升性能
val conf = new SparkConf().set("spark.reducer.maxSizeInFlight","96")
-
调节reduce端拉取数据重试次数
Spark Shuffle过程中,reduce task拉取属于自己的数据时,如果因为网络异常等原因导致失败会自动进行重试。对于那些包含了特别耗时的shuffle操作的作业,需要增加重试最大次数,以避免由于JVM的full gc或者网络不稳定等因素导致的数据拉取失败
val conf = new SparkConf().set("spark.shuffle.io.maxRetries","6")
-
调节reduce端拉取数据等待间隔
在Spark Shuffle过程中,reduce task拉取属于自己的数据时,如果因为网络异常等原因导致失败会自动进行重试,在一次失败后,会等待一定时间间隔再进行重试,可以通过加大时间间隔 以增加shuffle操作的稳定性
val conf = new SparkConf().set("spark.shuffle.io.retryWait","60s")
-
调节SortShuffle排序操作阈值
对于SortShuffleManager,如果shuffle reduce task的数量小于某一阈值则shuffle write 过程中不会进行排序操作,而是直接按照未经优化的HashShuffleManager的方式去写数据,但是最后会将每个task产生的所有临时磁盘文件合并成一个文件,并会创建单独的索引文件,当使用SortShuffleManager时,如果的确不需要排序操作,那么建议将这个参数调大,大于shuffle read task 的数量,那么此时map-side就不会进行排序,减少了排序的性能开销,但是这种方式下,依然会产生大量的磁盘文件,因此shuffle write性能有待提高
val conf = new Sparkconf().set("spark.shuffle.sort.bypassMergeThresld",“400”)
-