(1)尽量不要重复的创建RDD
我们在运行一个spark程序时候,就是对RDD的各种转换,多次使用到同一个RDD的时候要避免创建重复的RDD。
例如:
object sparkcore {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("sparkcore").setMaster("local")
val sc = new SparkContext(conf)
var list=List(1,2,3,4,5,6)
val rdd = sc.parallelize(list)
rdd.foreach(println(_))
val rdd1=sc.parallelize(list)
rdd1.foreach(println(_))
}
}
虽然这种操作看着不会做出来,但是当代码太过冗长的时候自己会忘记,创建多个RDD就需要多次计算同一份数据而创建相同的RDD,所以,避免重复的创建RDD可以增加性能
(2)能用一个RDD解决的事尽量用一个RDD
var list=List((3,"hadoop"),(2,"spark"),(1,"hbase"))
val rdd = sc.parallelize(list)
rdd.foreach(t=>println(t._2))
在这里,如果我们需要Hadoop,spark等值,可以通过其他包含这个数据的RDD获得,这样避免创建大量的RDD
(3)如果一个RDD要多次使用,那么把它持久化
每次对一个RDD执行一个算子操作时,都会重新从源头处计算一遍,计算出那个RDD来,然后再对这个RDD执行你的算子操作。这种方式的性能是很差的。所以将要多次使用的RDD保存下来,用的时候直接在内存中加载可以达到调优的目的。
(4)shuffle类算子不是你的最好选择
shuffle类算子就是将分布在不同节点上的相同的值拉取到一个节点上对其进行操作,这涉及网络传输,磁盘读写,所以,shuffle类算子很消耗性能。
例如可以使用broadcas加map代替join,这样就可以减少性能开销
(5)在map阶段进行预聚合,减少在shuffle阶段的的数据传输
使用reduceByKey或者aggregateByKey算子来替代掉groupByKey算子。因为reduceByKey和aggregateByKey算子都会使用用户自定义的函数对每个节点本地的相同key进行预聚合。而groupByKey算子是不会进行预聚合的,全量的数据会在集群的各个节点之间分发和传输,性能相对来说比较差。
(6)使用性能高的算子
使用mapPartitions替代普通map
使用foreachPartitions替代foreach
使用filter之后进行coalesce操作
使用repartitionAndSortWithinPartitions替代repartition与sort类操作
(7)broadcast
当算子使用到外部的变量时,如果这个变量比较大,应该将它广播,广播后task就可以共用一个变量了。
(8)使用kryo序列化功能
spark默认使用的是Java序列化机制,但这种序列化效率不高,spark支持使用kryo序列化机制,性能是Java序列化的10倍左右
(9)数据结构的优化
由于在开发的时候对象,数组,集合,字符串等所占的空间不一样,所以对数据结构的选择也可以提高性能
(10)数据本地性
Driver有个DGA会切分Application为stage 和 task,然后发送给executor,进行分配之前,都会计算出每个task要计算的是哪个分片数据,RDD的某个partition;Spark的task分配算法,优先,会希望每个task正好分配到它要计算的数据所在的节点,这样的话,就不用在网络间传输数据,所以可以通过设置数据的本地化级别来调优
美团点评技术团队的非常好的文章:
Spark性能优化指南——高级篇 —–> https://tech.meituan.com/spark-tuning-pro.html
Spark性能优化指南——基础篇 —–> https://tech.meituan.com/spark-tuning-basic.html