下面来看看更复杂的情况,比如,当调度器进行流水线执行(pipelining),或把多个 RDD 合并到一个步骤中时。当RDD 不需要混洗数据就可以从父节点计算出来时,调度器就会自动进行流水线执行。上一篇博文结尾处输出的谱系图使用不同缩进等级来展示 RDD 是否会在物理步骤中进行流水线执行。在物理执行时,执行计划输出的缩进等级与其父节点相同的 RDD 会与其父节点在同一个步骤中进行流水线执行。例如,当计算 counts 时,尽管有很多级父 RDD,但从缩进来看总共只有两级。这表明物理执行只需要两个步骤。由于执行序列中有几个连续的筛选和映射操作,所以这个例子中才出现了流水线执行。下图展示了计算 counts 这个RDD 时的两个执行步骤。

  

  除了流水线执行的优化,当一个 RDD 已经缓存在集群内存或磁盘上时,Spark 的内部调度器也会自动截短 RDD 谱系图。在这种情况下,Spark 会“短路”求值,直接基于缓存下来的 RDD 进行计算。还有一种截短 RDD 谱系图的情况发生在当 RDD 已经在之前的数据混洗中作为副产品物化出来时,哪怕该 RDD 并没有被显式调用 persist() 方法。这种内部优化是基于 Spark 数据混洗操作的输出均被写入磁盘的特性,同时也充分利用了 RDD 图的某些部分会被多次计算的事实。

  一个物理步骤会启动很多任务,每个任务都是在不同的数据分区上做同样的事情。任务内部的流程是一样的,如下所述。
   (1) 从数据存储(如果该 RDD 是一个输入 RDD)或已有 RDD(如果该步骤是基于已经缓存的数据)或数据混洗的输出中获取输入数据。
   (2) 执行必要的操作来计算出这些操作所代表的 RDD。例如,对输入数据执行 filter() 和map() 函数,或者进行分组或归约操作。
   (3) 把输出写到一个数据混洗文件中,写入外部存储,或者是发回驱动器程序(如果最终RDD 调用的是类似 count() 这样的行动操作)。

  归纳一下,Spark 执行时有下面所列的这些流程。
   • 用户代码定义RDD的有向无环图
    RDD 上的操作会创建出新的 RDD,并引用它们的父节点,这样就创建出了一个图。
   • 行动操作把有向无环图强制转译为执行计划
    当你调用 RDD 的一个行动操作时,这个 RDD 就必须被计算出来。这也要求计算出该RDD 的父节点。Spark 调度器提交一个作业来计算所有必要的 RDD。这个作业会包含一个或多个步骤,每个步骤其实也就是一波并行执行的计算任务。一个步骤对应有向无环图中的一个或多个 RDD,一个步骤对应多个 RDD 是因为发生了流水线执行。
   • 任务于集群中调度并执行
    步骤是按顺序处理的,任务则独立地启动来计算出 RDD 的一部分。一旦作业的最后一个步骤结束,一个行动操作也就执行完毕了。
  在一个给定的 Spark 应用中,由于需要创建一系列新的 RDD,因此上述阶段会连续发生很多次。

三、查找信息

  Spark 在应用执行时记录详细的进度信息和性能指标。这些内容可以在两个地方找到:Spark 的网页用户界面以及驱动器进程和执行器进程生成的日志文件中。

1、Spark网页用户界面

Spark 内建的网页用户界面是了解 Spark 应用的行为和性能表现的第一站。默认情况下,它在驱动器程序所在机器的 4040 端口上。

  关于Spark网页用户界面推荐一个博客,写得很详细[看图说话] 基于Spark UI性能优化与调试——初级篇。还有一个转载后的博客spark的UI界面

import org.apache.spark.SparkConf
import org.apache.spark.SparkContext object Spark_8 {
def main(args: Array[String]): Unit = { // 创建一个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)
sc.setLogLevel("WARN") // 设置日志显示级别
val input = sc.textFile("words.txt") // 读取输入文件
// 切分为单词并且删掉空行 如果大于0的话删除不掉空行
val tokenized = input.map(line=>line.split(" ")).filter(words=>words.size>1)
val counts = tokenized.map(words=>(words(0),1)).reduceByKey((a,b)=>a+b) // 提取出日志等级并进行计数 // 缓存RDD
counts.cache() println(input.toDebugString) // 通过toDebugString查看RDD的谱系
println("====================================================")
println(tokenized.toDebugString)
println("====================================================")
println(counts.toDebugString)
// countRDD已经缓存 第一次求值运行仍然需要两个步骤
counts.collect().foreach(println)
println("====================================================")
// 该次求值只有一个步骤
counts.collect().foreach(println) Thread.sleep(60000) // 为了访问 http://localhost:4040 线程睡眠 }
}

  在本地模式程序运行下里面的日志信息包含了Spark网页用户界面的URL。

19/04/21 20:23:12 INFO MemoryStore: MemoryStore started with capacity 884.7 MB
19/04/21 20:23:12 INFO SparkEnv: Registering OutputCommitCoordinator
19/04/21 20:23:12 INFO Utils: Successfully started service 'SparkUI' on port 4040.
19/04/21 20:23:12 INFO SparkUI: Bound SparkUI to 0.0.0.0, and started at http://192.168.11.1:4040
19/04/21 20:23:12 INFO Executor: Starting executor ID driver on host localhost

  注意,如果DAG Visualization这儿没有图片显示的话,那说明是浏览器的问题。

  

  我换了一个火狐浏览器,就能出来图片了。

  

2、驱动器进程和执行器进程的日志

  在某些情况下,用户需要深入研读驱动器进程和执行器进程所生成的日志来获取更多信息。日志会更详细地记录各种异常事件,例如内部的警告以及用户代码输出的详细异常信息。这些数据对于寻找错误原因很有用。

四、关键性能考量

1、并行度

  并行度会从两方面影响程序的性能。首先,当并行度过低时,Spark 集群会出现资源闲置的情况。比如,假设你的应用有 1000 个可使用的计算核心,但所运行的步骤只有 30 个任务,你就应该提高并行度来充分利用更多的计算核心。而当并行度过高时,每个分区产生的间接开销累计起来就会更大。评判并行度是否过高的标准包括任务是否是几乎在瞬间(毫秒级)完成的,或者是否观察到任务没有读写任何数据。

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

  举个例子,假设我们从 S3 上读取了大量数据,然后马上进行 filter() 操作筛选掉数据集中的绝大部分数据。默认情况下, filter() 返回的 RDD 的分区数和其父节点一样,这样可能会产生很多空的分区或者只有很少数据的分区。在这样的情况下,可以通过合并得到分区更少的 RDD 来提高应用性能。

2、序列化格式

  当 Spark 需要通过网络传输数据,或是将数据溢写到磁盘上时,Spark 需要把数据序列化为二进制格式。序列化会在数据进行混洗操作时发生,此时有可能需要通过网络传输大量数据。默认情况下,Spark 会使用 Java 内建的序列化库。Spark 也支持使用第三方序列化库 Kryo(https://github.com/EsotericSoftware/kryo),可以提供比 Java 的序列化工具更短的序列化时间和更高压缩比的二进制表示,但不能直接序列化全部类型的对象。几乎所有的应用都在迁移到 Kryo 后获得了更好的性能。

3、内存管理

  在各个执行器进程中,内存有以下所列几种用途。
   • RDD存储
   • 数据混洗与聚合的缓存区
   • 用户代码

  对于默认缓存策略的另一个改进是缓存序列化后的对象而非直接缓存。我们可以通过MEMORY_ONLY_SER 或者 MEMORY_AND_DISK_SER 的存储等级来实现这一点。缓存序列化后的对象会使缓存过程变慢,因为序列化对象也会消耗一些代价,不过这可以显著减少 JVM 的垃圾回收时间,因为很多独立的记录现在可以作为单个序列化的缓存而存储。

4、硬件供给

  提供给 Spark 的硬件资源会显著影响应用的完成时间。影响集群规模的主要参数包括分配给每个执行器节点的内存大小、每个执行器节点占用的核心数、执行器节点总数,以及用来存储临时数据的本地磁盘数量。

  这篇博文主要来自《Spark快速大数据分析》这本书里面的第八章,内容有删减,还有本书的一些代码的实验结果。

Spark学习之Spark调优与调试(二)的更多相关文章

  1. Spark学习之Spark调优与调试(7)

    Spark学习之Spark调优与调试(7) 1. 对Spark进行调优与调试通常需要修改Spark应用运行时配置的选项. 当创建一个SparkContext时就会创建一个SparkConf实例. 2. ...

  2. 【原创 Hadoop&Spark 动手实践 8】Spark 应用经验、调优与动手实践

    [原创 Hadoop&Spark 动手实践 7]Spark 应用经验.调优与动手实践 目标: 1. 了解Spark 应用经验与调优的理论与方法,如果遇到Spark调优的事情,有理论思考框架. ...

  3. Spark面试题(八)——Spark的Shuffle配置调优

    Spark系列面试题 Spark面试题(一) Spark面试题(二) Spark面试题(三) Spark面试题(四) Spark面试题(五)--数据倾斜调优 Spark面试题(六)--Spark资源调 ...

  4. Spark学习之Spark Streaming(9)

    Spark学习之Spark Streaming(9) 1. Spark Streaming允许用户使用一套和批处理非常接近的API来编写流式计算应用,这就可以大量重用批处理应用的技术甚至代码. 2. ...

  5. Spark学习之Spark SQL(8)

    Spark学习之Spark SQL(8) 1. Spark用来操作结构化和半结构化数据的接口--Spark SQL. 2. Spark SQL的三大功能 2.1 Spark SQL可以从各种结构化数据 ...

  6. Spark学习之Spark调优与调试(一)

    一.使用SparkConf配置Spark 对 Spark 进行性能调优,通常就是修改 Spark 应用的运行时配置选项.Spark 中最主要的配置机制是通过 SparkConf 类对 Spark 进行 ...

  7. Spark学习笔记6:Spark调优与调试

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

  8. Spark调优与调试

    1.使用SparkConf配置Spark (1)在java中使用SparkConf创建一个应用: SparkConf conf =;i++){ javaBean bean =new javaBean( ...

  9. 【Spark】Sparkstreaming-性能调优

    Sparkstreaming-性能调优 Spark Master at spark://node-01:7077 sparkstreaming 线程 数量_百度搜索 streaming中partiti ...

随机推荐

  1. andorid下从相册选取/拍照选取一张相片并剪切

    http://www.2cto.com/kf/201401/270144.html 在Android编程中,从相册选取或是拍照选取一张照片然后对其进行剪切的需求非常的多 之前的一篇文章只说到如何从相册 ...

  2. 推荐个Mac OSX下的Code Editor:Atom

    首先只是当Editor用,不是整成IDE级. 先说几个大家耳熟能详的: 1.Sublime,Sublime在Mac下的安装并不完全,CLI启动需要自己ln个链接.还有一些其他原因,比如Packages ...

  3. JAVA 语法2

    1.算术运算符 运算符 运算规则 范例 结果 + 正号 +3 3 + 加 2+3 5 + 连接字符串 "中"+"国" "中国" - 负号 i ...

  4. 用js来实现那些数据结构—目录

    首先,有一点要声明,下面所有文章的所有内容的代码,都不是我一个人独立完成的,它们来自于一本叫做<学习JavaScript数据结构和算法>(第二版),人民邮电出版社出版的这本书.github ...

  5. D3中的each() 以及svg defs元素 clipPath的使用

    each() 方法允许我们定制对选择集中DOM元素的处理行为: selection . each ( func ) 参数 func 是调用者定义的函数,在d3中被称为 访问器/accessor . d ...

  6. css基础--深入理解弹性盒flex布局

    欢迎访问我的个人博客:http://www.xiaolongwu.cn 1. 前言 flex弹性盒,是一种布局方式,当页面需要适应不同的屏幕大小以及设备类型时,它依然能确保元素 拥有更恰当的排布行为, ...

  7. “史上更难就业季”暴露出啥隐情?

      如果说,2013年中国高校毕业生达到699万,被称为"史上最难就业季".那么2014年将成为去年之后的"更难就业季".据最新资料显示,2014年应届大学毕业 ...

  8. PAT1129:Recommendation System

    1129. Recommendation System (25) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue ...

  9. JS/jquery实现鼠标控制页面元素显隐

    最近网站要上一个活动广告横幅,当用户鼠标划过时显隐二维码.像这种鼠标事件控制页面元素显隐的情况,码农们会经常遇到,可以通过javascript或jquery代码实现,下面就几种常见需求一起归纳一下. ...

  10. vs2017 x64 ibatis.net 平台调用 Oracle.DataAccess, Version=2.112.1.0, Culture=neutral, PublicKeyToken=89b483f429c47342 x64

    遇到的问题: 1.x86无法调用x64 2.调用ibatis.net的providers.config无法通过节点反射查找Oracle.DataAccess, Version=2.112.1.0, C ...