Spark(二)-- Spark 底层逻辑 (六) -- 简介

6. Spark 底层逻辑 -- 简介

导读

  1. 从部署图了解 Spark 部署了什么, 有什么组件运行在集群中

  2. 通过对 WordCount 案例的解剖, 来理解执行逻辑计划的生成

  3. 通过对逻辑执行计划的细化, 理解如何生成物理计划

如无特殊说明, 以下部分均针对于 Spark Standalone 进行介绍

通过wordCount案例来从“部署情况”、“逻辑执行图”、“物理执行图”来初步了解 spark的底层逻辑

部署情况

在 Spark 部分的底层执行逻辑开始之前, 还是要先认识一下 Spark 的部署情况, 根据部署情况, 从而理解如何调度.

WX20190513 233552

针对于上图, 首先可以看到整体上在集群中运行的角色有如下几个:

  • Master Daemon

    负责管理 Master 节点, 协调资源的获取, 以及连接 Worker 节点来运行 Executor, 是 Spark 集群中的协调节点

  • Worker Daemon

    Workers 也称之为叫 Slaves, 是 Spark 集群中的计算节点, 用于和 Master 交互并管理 Executor.

    当一个 Spark Job 提交后, 会创建 SparkContext, 后 Worker 会启动对应的 Executor.

  • Executor Backend

    上面有提到 Worker 用于控制 Executor 的启停, 其实 Worker 是通过 Executor Backend 来进行控制的, Executor Backend 是一个进程(是一个 JVM 实例), 持有一个 Executor 对象

另外在启动程序的时候, 有三种程序需要运行在集群上:

  • Driver

    Driver 是一个 JVM 实例, 是一个进程, 是 Spark Application 运行时候的领导者, 其中运行了 SparkContext.

    Driver 控制 Job 和 Task, 并且提供 WebUI.

  • Executor

    Executor 对象中通过线程池来运行 Task, 一个 Executor 中只会运行一个 Spark Application 的 Task, 不同的 Spark Application 的 Task 会由不同的 Executor 来运行

案例

因为要理解执行计划, 重点不在案例, 所以本节以一个非常简单的案例作为入门, 就是我们第一个案例 WordCount

  @Test
  def wordCount():Unit = {
    //1.创建sc对象
    val conf = new SparkConf().setMaster("local[6]").setAppName("wordCount_source")
    val sc = new SparkContext(conf)

    //2.创建数据集
    val textRDD: RDD[String] = sc.parallelize(Seq("hadoop spark","hadoop flume","spark sqoop"))

    //3.数据处理
    //3.1拆词
    val splitRDD: RDD[String] = textRDD.flatMap(_.split(" "))
    //3.2赋予初始词频
    val tupleRDD: RDD[(String, Int)] = splitRDD.map((_,1))
    //3.3聚合统计词频
    val reduceRDD: RDD[(String, Int)] = tupleRDD.reduceByKey(_ + _)
    //3.4将结果转为字符串
    val strRDD: RDD[String] = reduceRDD.map(item => s"${item._1},${item._2}")

    //4.结果获取
    strRDD.collect().foreach(println(_))

    //5.关闭sc,执行
    sc.stop()

    //逻辑执行图:描述数据如何流动,如何计算
    // textRDD -> splitRDD -> tupleRDD -> reduceRDD -> strRDD
    // 预执行,生成RDD逻辑关系  println(strRDD.toDebugString)
    /*
    (6) MapPartitionsRDD[4] at map at SourceAnalysis.scala:26 []
    |  ShuffledRDD[3] at reduceByKey at SourceAnalysis.scala:24 []
    +-(6) MapPartitionsRDD[2] at map at SourceAnalysis.scala:22 []
    |  MapPartitionsRDD[1] at flatMap at SourceAnalysis.scala:20 []
    |  ParallelCollectionRDD[0] at parallelize at SourceAnalysis.scala:16 []
    */

  }

整个案例的运行过程大致如下:

  1. 通过代码的运行, 生成对应的 RDD 逻辑执行图

  2. 通过 Action 操作, 根据逻辑执行图生成对应的物理执行图, 也就是 Stage 和 Task

  3. 将物理执行图运行在集群中

逻辑执行图

对于上面代码中的 reduceRDD 如果使用 toDebugString 打印调试信息的话, 会显式如下内容

(6) MapPartitionsRDD[4] at map at WordCount.scala:20 []
 |  ShuffledRDD[3] at reduceByKey at WordCount.scala:19 []
 +-(6) MapPartitionsRDD[2] at map at WordCount.scala:18 []
    |  MapPartitionsRDD[1] at flatMap at WordCount.scala:17 []
    |  ParallelCollectionRDD[0] at parallelize at WordCount.scala:16 []

根据这段内容, 大致能得到这样的一张逻辑执行图

20190515002803

其实 RDD 并没有什么严格的逻辑执行图和物理执行图的概念, 这里也只是借用这个概念, 从而让整个 RDD 的原理可以解释, 好理解.

对于 RDD 的逻辑执行图, 起始于第一个入口 RDD 的创建, 结束于 Action 算子执行之前, 主要的过程就是生成一组互相有依赖关系的 RDD, 其并不会真的执行, 只是表示 RDD 之间的关系, 数据的流转过程.

物理执行图

当触发 Action 执行的时候, 这一组互相依赖的 RDD 要被处理, 所以要转化为可运行的物理执行图, 调度到集群中执行.

因为大部分 RDD 是不真正存放数据的, 只是数据从中流转, 所以, 不能直接在集群中运行 RDD, 要有一种 Pipeline 的思想, 需要将这组 RDD 转为 Stage 和 Task, 从而运行 Task, 优化整体执行速度.

以上的逻辑执行图会生成如下的物理执行图, 这一切发生在 Action 操作被执行时.

20190515235205

从上图可以总结如下几个点

  • 20190515235442 在第一个 Stage 中, 每一个这样的执行流程是一个 Task, 也就是在同一个 Stage 中的所有 RDD 的对应分区, 在同一个 Task 中执行

  • Stage 的划分是由 Shuffle 操作来确定的, 有 Shuffle 的地方, Stage 断开

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值