开发调优
最基本的Spark性能优化,就是要优化你的代码。Spark中rdd内部的转换关系是一个DAG(有向无环图),只有出发了action 算子才开始计算。开始可以画出计算pipeline,写得多了脑子自然会形成计算的pipeline,在开发过程中,时时刻刻都要注意一些性能优化的基本原则。
原则一:避免创建重复的RDD,尽可能复用同一个RDD
对于同一份数据不要创建多个RDD,对不同的数据执行算子操作时要尽可能地复用一个RDD。
原则二:对多次使用的RDD进行持久化
前面已经提到Spark中rdd内部的转换关系是一个DAG,因此对于一个RDD执行多次算子时,都会重新从源头处计算一遍,这种方式的性能是很差的。如下图所示,其中D和E代表action算子,在计算D和E时要分别从A开始计算。
最好的方法就是对C进行持久化,此时Spark就会将数据保存到内存或者磁盘中,以后每次对C这个RDD进行算子操作时,都会直接从内存或磁盘中提取持久化的RDD数据,不会从源头处重新计算一遍。
原则三:尽量避免使用shuffle类算子
Spark作业运行过程中,最消耗性能的地方就是shuffle过程。shuffle过程就是将分布在集群中多个节点上的同一个key,拉取到同一个节点上,进行groupby或join等操作,reduceByKey、join、distinct、repartition等都属于shuffle算子。
至于什么是shuffle,引用Spark核心设计思想的经典论文“Resilient Distributed Datasets: A Fault-Tolerant Abstraction for In-Memory Cluster Computing”可以解释。
RDD就是一个不可变的带分区的记录集合,Spark提供了RDD上的两类操作,转换和动作。转换是用来定义一个新的RDD,包括map, flatMap, filter, union, sample, join, groupByKey, cogroup, ReduceByKey, cros, sortByKey, mapValues等,动作是返回一个结果,包括collect, reduce, count, save, lookupKey。
shuffle顾名思义就是被打散,是否被shuffle就看计算后对应多少分区,那么:
1.如果一个RDD的依赖的每个分区只依赖另一个RDD的同一个分区,就是narow,如图上的map、filter、union等,这样就不需要进行shuffle,同时还可以按照流水线的方式,把一个分区上的多个操作放在一个Task里进行。
2.如果一个RDD的每个分区需要依赖另一个RDD的所有分区,就是wide,如图上的groupbykey,这样的依赖需要进行shuffle,运算量倍增。