概述
在Spark的使用中,无论是直接以HDFS为数据源,或是以HBase类数据仓库为承载源,除去业务逻辑方面的增删改查及数据过滤,最终难免面临性能调优的环节。而事实上Spark的性能调优是由很多部分组成的,需要根据业务场景及数据情况,对各个环节进行多个方面的调节和优化,以期获得最佳的运行性能。
当然现实的情况下是,大部分开发人员不具备如此专业复杂的调优能力,最好的结果是通过调节几个参数就可以对性能起到立竿见影效果。关于Spark性能调优,网间有许许多多的博文,本篇文章也仅仅是参考前人经验,撇除一些复杂内容,保留一些对开发人员友好的部分进行阐述。
Spark性能调优
Spark作业的性能优化主要分为四个方向,开发调优、资源调优、数据倾斜调优和shuffle调优几个部分。
1.开发调优
开发调优是Spark调优过程中最基础的一步,也是每个开发人员应该遵守的一些基本原则。包括:RDD的lineage设计、算子的合理使用、特殊操作的优化等。
简单俩说,需要注意的原则有一下几个:
- 避免创建重复的RDD
- 尽可能复用同一个RDD
- 对多次使用的RDD进行持久化
- 尽量避免使用shuffle类算子
- 使用map-side预聚合的shuffle操作
- 使用高性能的算子
- 广播大变量
- 使用Kryo优化序列化性能
- 优化数据结构
1,2,3
关于复用RDD,指的是检查代码中是否对同一份数据来源创建了重复的RDD, 同时对某些可以简单转换得到的RDD,也要尽可能避免创建中间RDD。另外就是对多次使用到的RDD进行持久化。(调用cache
或者persist
方法)
4,5,6
所关注的点其实都汇聚在网络传输和磁盘IO上。因为shuffle过程,简单来说就是将分布在集群中多个节点上的同一个key,拉取到同一个节点上进行聚合,期间涉及大量的磁盘IO和网络传输,所以shuffle过程往往是Spark作业的性能消耗大户。因此在程序开发中药尽可能的避免使用此类算子,在必要的时候要尝试转换shuffle操作至‘map端’,例如使用reduceByKey
或者aggregateByKey
来替代掉groupByKey
,这类算子会在shuffle之前将本地数据进行聚合,从而降低网络传输和磁盘IO的性能开销。常用的高性能算子替代方案包括:mapPartitions<<<map
、foreachPartitions<<<foreach
,此二类皆是对分区进行操作,避免一条一条的数据处理。同样的在filter
过滤数据之后进行coalesce
操作合理减少分区数,可以降低低负荷task数据量,进而提升整体的性能。 官方还建议使用repartitionAndSortWithinPartitions
替代repartition
与sort
类操作,以期在重新分区的同时完成排序。
7,8
中广播大变量是一个非常有用的操作,可以有效的降低网络传输及内存占用。因为在默认情况下,Spark会将使用到的外部变量复制多个变量副本,通过网络传输到各个task中,大变量将会浪费大量珍贵的内存空间。而广播后的变量,每个Executor的内存中只有一个变量副本,而task执行时共享所属Executor中的那份变量副本。val Broadcast_var = sc.broadcast(var_tmp)
。而使用Kryo进行序列化则是一个重要辅助优化,在广播大变量以及RDD持久化中,Kryo序列化机制比默认的Java序列化机制,性能高10倍左右。不过Kryo要求注册所有需要进行序列化的自定义类型。
// 设置序列化器为KryoSerializer
conf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
// 注册要序列化的自定义类型。
conf.registerKryoClasses(Array(classOf[MyClass1], classOf[MyClass2]))
9
优化数据结构一般不容易实现,不再详述。
可以看到,其实开发调优部分的要求不难满足,但往往能起到很好的效果,因此在日常开发过程中一定要时常联系这些准则,改进自己的写法,事半功倍。
2.资源调优
相对于开发调优,资源调优的门槛又高了一些,这需要开发人员对Spark资源使用原理有明确的概念,包括作业启动时资源申请、Stage划分以及作业分发等一些内容。
先把spark-submit
时常用的一些参数罗列如下:
- –master
- –driver-memory
- –num-executors
- –executor-cores
- –executor-memor
- –conf spark.default.parallelism
- –conf spark.storage.memoryFraction
- –conf spark.shuffle.memoryFraction
首先是跟Driver
有关的两个参数,--master
决定Driver的运行位置,分为本地和集群,--driver-memory
设置driver进程的内存大小。driver所做的事情依次为&#