Spark调优与调试

Posted on 2018-06-27 14:56  打杂滴  阅读(266)  评论(0编辑  收藏  举报

使用SparkConf配置Spark

对Spark 进行性能调优,通常就是修改Spark 应用的运行时配置选项。Spark 中最主要的配置机制是通过SparkConf 类对Spark 进行配置。当创建出一个SparkContext 时,就需要创建出一个SparkConf 的实例.

在Scala 中使用SparkConf 创建一个应用

// 创建一个conf对象
val conf = new SparkConf()
conf.set("spark.app.name", "My Spark App")
conf.set("spark.master", "local[4]")
conf.set("spark.ui.port", "36000") // 重载默认端口配置
// 使用这个配置对象创建一个SparkContext
val sc = new SparkContext(conf)

SparkConf 类其实很简单:SparkConf 实例包含用户要重载的配置选项的键值对。Spark 中的每个配置选项都是基于字符串形式的键值对。要使用创建出来的SparkConf 对象,可以调用set() 方法来添加配置项的设置,然后把这个对象传给SparkContext 的构造方法。除了set() 之外,SparkConf 类也包含了一小部分工具方法,可以很方便地设置部分常用参数。例如,在前面的三个例子中,你也可以调setAppName() 和setMaster() 来分别设置spark.app.name 和spark.master 的配置值。

Spark 提供了toDebugString() 方法来查看RDD 的谱系。

scala> rdd00.collect

res29: Array[Int] = Array(80, 95, 60, 86, 30, 99, 78, 101, 31, 54, 79, 85, 3000)

scala> val rdd001=rdd00.filter(x=>x>80)

rdd001: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[30] at filter at <console>:30

scala> rdd001.toDebugString
res30: String =
(4) MapPartitionsRDD[30] at filter at <console>:30 []
 |  ParallelCollectionRDD[24] at makeRDD at <console>:28 []

可以看出通过 makeRDD创建了RDD,然后通过filter生成当前RDD

spark 执行时有下面这些流程:
• 用户代码定义RDD的有向无环图

 RDD 上的操作会创建出新的RDD,并引用它们的父节点,这样就创建出了一个图。
• 行动操作把有向无环图强制转译为执行计划

当你调用RDD 的一个行动操作时,这个RDD 就必须被计算出来。这也要求计算出该RDD 的父节点。Spark 调度器提交一个作业来计算所有必要的RDD。这个作业会包含一个或多个步骤,每个步骤其实也就是一波并行执行的计算任务。一个步骤对应有向无环图中的一个或多个RDD,一个步骤对应多个RDD 是因为发生了流水线执行。

• 任务于集群中调度并执行
步骤是按顺序处理的,任务则独立地启动来计算出RDD 的一部分。一旦作业的最后一个步骤结束,一个行动操作也就执行完毕了。

Spark网页用户界面

作业页面:步骤与任务的进度和指标

本页面经常用来评估一个作业的性能表现。我们可以着眼于组成作业的所有步骤,看看是不是有一些步骤特别慢,或是在多次运行同一个作业时响应时间差距很大。如果你遇到了格外慢的步骤,你可以点击进去来更好地理解该步骤对应的是哪段用户代码。

下面的步骤页面也定位性能问题。在Spark 这样的并行数据系统中,数据倾斜是导致性能问题的常见原因之一。

 

当看到少量的任务相对于其他任务需要花费大量时间的时候,一般就是发生了数据倾斜。步骤页面可以帮助我们发现数据倾斜,我们只需要查看所有任务各项指标的分布情况就可以了

存储页面:已缓存的RDD的信息

scala> import org.apache.spark.storage.StorageLevel

import org.apache.spark.storage.StorageLevel

scala> rdd00.persist(StorageLevel.DISK_ONLY)

res44: rdd00.type = ParallelCollectionRDD[24] at makeRDD at <console>:28

scala> rdd00.collect
res49: Array[Int] = Array(80, 95, 60, 86, 30, 99, 78, 101, 31, 54, 79, 85, 3000)

 执行器页面:应用中的执行器进程列表

 

关键性能考量

并行度

在物理执行期间,RDD 会被分为一系列的分区,每个分区都是整个数据的子集。当Spark 调度并运行任务时,Spark 会为每个分区中的数据创建出一个任务。该任务在默认情况下会需要集群中的一个计算核心来执行。Spark 也会针对RDD 直接自动推断出合适的并行度,这对于大多数用例来说已经足够了。输入RDD 一般会根据其底层的存储系统选择并行度。从数据混洗后的RDD 派生下来的RDD 则会采用与其父RDD 相同的并行度。

并行度会从两方面影响程序的性能。首先,当并行度过低时,Spark 集群会出现资源闲置的情况.而当并行度过高时,每个分区产生的间接开销累计起来就会更大。

Spark 提供了两种方法来对操作的并行度进行调优。第一种方法是在数据混洗操作时,使用参数的方式为混洗后的RDD 指定并行度。第二种方法是对于任何已有的RDD,可以进行重新分区来获取更多或者更少的分区数。重新分区操作通过repartition() 实现,该操作会把RDD 随机打乱并分成设定的分区数目。如果你确定要减少RDD 分区,可以使用coalesce() 操作。由于没有打乱数据,该操作比repartition() 更为高效。如果你认为当前的并行度过高或者过低,可以利用这些方法对数据分布进行重新调整。

序列化格式

当Spark 需要通过网络传输数据,或是将数据溢写到磁盘上时,Spark 需要把数据序列化为二进制格式。序列化会在数据进行混洗操作时发生,此时有可能需要通过网络传输大量数据。

默认情况下,Spark 会使用Java 内建的序列化库。Spark 也支持使用第三方序列化库Kryo(https://github.com/EsotericSoftware/kryo),可以提供比Java 的序列化工具更短的序列化时间和更高压缩比的二进制表示,但不能直接序列化全部类型的对象。几乎所有的应用都在迁移到Kryo 后获得了更好的性能。

使用Kryo 序列化工具并注册所需类:

val conf = new SparkConf()
conf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")

// 严格要求注册类
conf.set("spark.kryo.registrationRequired", "true")
conf.registerKryoClasses(Array(classOf[MyClass], classOf[MyOtherClass]))

内存管理

在各个执行器进程中,内存有以下所列几种用途。

RDD存储;数据混洗与聚合的缓存区;用户代码

 

Copyright © 2024 打杂滴
Powered by .NET 8.0 on Kubernetes