Spark原理——逻辑执行图

这篇具有很好参考价值的文章主要介绍了Spark原理——逻辑执行图。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

逻辑执行图

  • 明确逻辑计划的边界

    在 Action 调用之前,会生成一系列的RDD,这些RDD之间的关系,其实就是整个逻辑计划

    val conf= new SparkConf().setMaster("local[6]").setAppName("wordCount_source")
    val sc= new SparkContext(conf)
    
    val textRDD=sc.parallelize(Seq("Hadoop Spark", "Hadoop Flume", "Spark Sqoop"))
    val splitRDD=textRDD.flatMap(_.split(" "))
    val tupleRDD=splitRDD.map((_, 1))
    val reduceRDD=tupleRDD.reduceByKey(_ + _)
    val strRDD=reduceRDD.map(item => s"${item._1},${item._2}")
    
    println(strRDD.toDebugString)
    strRDD.collect.foreach(item =>println(item))
    

    例如上述代码,如果生成逻辑计划的,会生成效如下一些RDD,这些RDD是相互关联的,这些RDD之间,其实本质上生成的就是一个 计算链

    Spark原理——逻辑执行图,Spark,大数据,spark,大数据

    接下来, 采用迭代渐进式的方式, 一步一步的查看一下整体上的生成过程

  • RDD 如何生成

    • **textFile算子的背后**

      研究 RDD 的功能或者表现的时候, 其实本质上研究的就是 RDD中的五大属性, 因为 RDD透过五大属性来提供功能和表现, 所以如果要研究 textFile 这个算子, 应该从五大属性着手, 那么第一步就要看看生成的 RDD是什么类型的 RDD

      1. **textFile 生成的是 HadoopRDD**

        Spark原理——逻辑执行图,Spark,大数据,spark,大数据

        Spark原理——逻辑执行图,Spark,大数据,spark,大数据

        Spark原理——逻辑执行图,Spark,大数据,spark,大数据

      2. **HadoopRDDPartitions对应了 HDFSBlocks**

        Spark原理——逻辑执行图,Spark,大数据,spark,大数据

        其实本质上每个 HadoopRDDPartition都是对应了一个 HadoopBlock, 通过 InputFormat 来确定 Hadoop中的 Block 的位置和边界, 从而可以供一些算子使用

        Spark原理——逻辑执行图,Spark,大数据,spark,大数据

      3. HadoopRDDcompute函数就是在读取 HDFS中的 Block

        本质上, compute 还是依然使用 InputFormat来读取 HDFS中对应分区的 Block

      4. textFile这个算子生成的其实是一个 MapPartitionsRDD

        textFile这个算子的作用是读取 HDFS上的文件, 但是 HadoopRDD中存放是一个元组, 其 Key是行号, 其 ValueHadoop中定义的 Text对象, 这一点和 MapReduce程序中的行为是一致的

        但是并不适合 Spark 的场景, 所以最终会通过一个 map算子, 将 (LineNum, Text)转为 String形式的一行一行的数据, 所以最终 textFile这个算子生成的 RDD 并不是 HadoopRDD, 而是一个 MapPartitionsRDD

    • map算子的背后

      Spark原理——逻辑执行图,Spark,大数据,spark,大数据

      • map算子生成了 MapPartitionsRDD

         val conf= new SparkConf().setMaster("local[6]").setAppName("wordCount_source")
        val sc= new SparkContext(conf)
        
        val rdd=sc.parallelize(Seq("Hadoop Spark", "Hadoop Flume", "Spark Sqoop"))
        val rdd1=rdd.flatMap(_.split(" "))
        val rdd2=rdd1.map((_, 1))
        

        由源码可知, 当 val rdd2 = rdd1.map() 的时候, 其实生成的新 RDD 是 rdd2, rdd2 的类型是 MapPartitionsRDD, 每个 RDD 中的五大属性都会有一些不同, 由 map 算子生成的 RDD 中的计算函数, 本质上就是遍历对应分区的数据, 将每一个数据转成另外的形式

      • MapPartitionsRDD的计算函数是 collection.map( function )

        真正运行的集群中的处理单元是 Task, 每个 Task 对应一个 RDD 的分区, 所以 collection 对应一个 RDD 分区的所有数据, 而这个计算的含义就是将一个 RDD 的分区上所有数据当作一个集合, 通过这个 Scala 集合的 map 算子, 来执行一个转换操作, 其转换操作的函数就是传入 map 算子的 function

      • 传入 map算子的函数会被清理

        Spark原理——逻辑执行图,Spark,大数据,spark,大数据

        这个清理主要是处理闭包中的依赖, 使得这个闭包可以被序列化发往不同的集群节点运行

    • flatMap算子的背后

      Spark原理——逻辑执行图,Spark,大数据,spark,大数据

      flatMapmap算子其实本质上是一样的, 其步骤和生成的 RDD都是一样, 只是对于传入函数的处理不同, mapcollect.map( function )flatMapcollect.flatMap( function )

      从侧面印证了, 其实 Spark中的 flatMapScala基础中的 flatMap其实是一样的

      val conf= new SparkConf().setMaster("local[6]").setAppName("wordCount_source")
      val sc= new SparkContext(conf)
      
      val textRDD=sc.parallelize(Seq("Hadoop Spark", "Hadoop Flume", "Spark Sqoop"))
      val splitRDD=textRDD.flatMap(_.split(" "))
      val tupleRDD=splitRDD.map((_, 1))
      val reduceRDD=tupleRDD.reduceByKey(_ + _)
      val strRDD=reduceRDD.map(item => s"${item._1},${item._2}")
      
      // println(strRDD.toDebugString)
      strRDD.collect.foreach(item =>println(item))
      

      textRDDsplitRDDtupleRDD

      textRDDsplitRDD再到 tupleRDD的过程, 其实就是调用 mapflatMap算子生成新的 RDD的过程, 所以如下图所示, 就是这个阶段所生成的逻辑计划

  • RDD 之间有哪些依赖关系

    • 前置说明

      • 什么是RDD之间的依赖关系?

        Spark原理——逻辑执行图,Spark,大数据,spark,大数据

        • 什么是关系(依赖关系)?

          从算子视角上来看,splitRDD 通过 map 算子得到了 tupleRDD ,所以 splitRDD 和 tupleRDD 之间的关系是 map, 但是仅仅这样说,会不够全面,从细节上来看,RDD只是数据和关于数据的计算,而具体执这种计算得出结果的是一个神秘的其它组件,所以,这两个 RDD 的关系可以表示为 splitRDD 的数据通过 map 操作,被传入 tupleRDD ,这是它们之间更细化的关系

          但是 RDD 这个概念本身并不是数据容器,数据真正应该存放的地方是 RDD 的分区,所以如果把视角放在数据这一层面上的话,直接讲这两个 RDD 之间有关系是不科学的,应该从这两个 RDD 的分区之间的关系来讨论它们之间的关系

        • 那这些分区之间是什么关系?

          如果仅仅说 splitRDD 和 tupleRDD 之间的话,那它们的分区之间就是一对一的关系

          但是 tupleRDD 到 reduceRDD 呢?tupleRDD 通过算子 reduceByKey 生成 reduceRDD ,而这个算子是一个 Shuffle 操作,Shuff1e 操作的两个 RDD 的分区之间并不是一对一,reduceByKey 的一个分区对应 tupleRDD 的多个分区

      • reduceByKey 算子会生成 ShuffledRDD

        reduceByKey 是由算子 combineByKey 来实现的,combineByKey 内部会创建 ShuffledRDD 返回,而整个reduceByKey操作大致如下过程

        Spark原理——逻辑执行图,Spark,大数据,spark,大数据

        去掉两个 reducer 端分区,只留下一个的话,如下

        Spark原理——逻辑执行图,Spark,大数据,spark,大数据

        所以, 对于 reduceByKey 这个 Shuffle 操作来说, reducer 端的一个分区, 会从多个 mapper 端的分区拿取数据, 是一个多对一的关系

        至此为止, 出现了两种分区见的关系了, 一种是一对一, 一种是多对一

    • 窄依赖

      窄依赖(NarrowDependency)

      假如 rddB = rddA.transform(…), 如果 rddB 中一个分区依赖 rddA 也就是其父 RDD 的少量分区, 这种 RDD 之间的依赖关系称之为窄依赖

      换句话说, 子 RDD 的每个分区依赖父 RDD 的少量个数的分区, 这种依赖关系称之为窄依赖

      @Test
      def narrowDependency(): Unit = {
        // 需求:求得两个 RDD 之间的笛卡尔积
      
        // 1. 生成 RDD
        val conf = new SparkConf().setMaster("local[6]").setAppName("cartesian")
        val sc = new SparkContext(conf)
      
        val rddA = sc.parallelize(Seq(1, 2, 3))
        val rddB = sc.parallelize(Seq("a", "b"))
      
        // 2. 计算
        val rddC = rdd1.cartesian(rdd2)
      
        // 3. 结果获取
        rddC.collect().foreach(print(_))
      
        sc.stop()
      }
      // 运行结果: (1,a) (1,b) (2,a) (2,b) (3,a) (3,b)
      
      • 上述代码的 cartesian 是求得两个集合的笛卡尔积
      • 上述代码的运行结果是 rddA 中每个元素和 rddB 中的所有元素结合, 最终的结果数量是两个 RDD 数量之和
      • rddC 有两个父 RDD, 分别为 rddA 和 rddB

      对于 cartesian 来说, 依赖关系如下

      Spark原理——逻辑执行图,Spark,大数据,spark,大数据

      上述图形中清晰展示如下现象

      • rddC 中的分区数量是两个父 RDD 的分区数量之乘积
      • rddA 中每个分区对应 rddC 中的两个分区 (因为 rddB 中有两个分区), rddB 中的每个分区对应 rddC 中的三个分区 (因为 rddA 有三个分区)

      它们之间是窄依赖, 事实上在 cartesian 中也是 NarrowDependency 这个所有窄依赖的父类的唯一一次直接使用, 为什么呢?

      因为所有的分区之间是拷贝关系, 并不是 Shuffle 关系

      • rddC 中的每个分区并不是依赖多个父 RDD 中的多个分区
      • rddC 中每个分区的数量来自一个父 RDD 分区中的所有数据, 是一个 FullDependence, 所以数据可以直接从父 RDD 流动到子 RDD
      • 不存在一个父 RDD 中一部分数据分发过去, 另一部分分发给其它的 RDD
    • 宽依赖

      宽依赖(ShuffleDependency

      并没有所谓的宽依赖, 宽依赖应该称作为 ShuffleDependency

      在 ShuffleDependency 的类声明上如下写到

      Represents a dependency on the output of a shuffle stage.
      

      上面非常清楚的说道, 宽依赖就是 Shuffle 中的依赖关系, 换句话说, 只有 Shuffle 产生的地方才是宽依赖

      那么宽窄依赖的判断依据就非常简单明确了, 是否有 Shuffle ?

      举个 reduceByKey 的例子, rddB = rddA.reduceByKey( (curr, agg) ⇒ curr + agg ) 会产生如下的依赖关系

      Spark原理——逻辑执行图,Spark,大数据,spark,大数据

      Spark原理——逻辑执行图,Spark,大数据,spark,大数据

      • rddB 的每个分区都几乎依赖 rddA 的所有分区
      • 对于 rddA 中的一个分区来说, 其将一部分分发给 rddB 的 p1, 另外一部分分发给 rddB 的 p2, 这不是数据流动, 而是分发
    • 如何分辨宽窄依赖 ?

      其实分辨宽窄依赖的本身就是在分辨父子 RDD 之间是否有 Shuffle, 大致有以下的方法

      • 如果是 Shuffle, 两个 RDD 的分区之间不是单纯的数据流动, 而是分发和复制
      • 一般 Shuffle 的子 RDD 的每个分区会依赖父 RDD 的多个分区

      先看是否一对一>是就是窄依赖,如果不是一对一,是多对一>不能确定,再继续判断

      但是这样判断其实不准确, 如果想分辨某个算子是否是窄依赖, 或者是否是宽依赖, 则还是要取决于具体的算子, 例如想看 cartesian 生成的是宽依赖还是窄依赖, 可以通过如下步骤

      1. 查看 map 算子生成的 RDD

        Spark原理——逻辑执行图,Spark,大数据,spark,大数据

        Spark原理——逻辑执行图,Spark,大数据,spark,大数据

        Spark原理——逻辑执行图,Spark,大数据,spark,大数据

      2. 进去 RDD 查看 getDependence 方法

        Spark原理——逻辑执行图,Spark,大数据,spark,大数据

        Spark原理——逻辑执行图,Spark,大数据,spark,大数据

        Spark原理——逻辑执行图,Spark,大数据,spark,大数据

        Spark原理——逻辑执行图,Spark,大数据,spark,大数据
        724024159.png?origin_url=Untitled%2520180.png&pos_id=img-mqipe2i4-1704890196048)

    • 常见的窄依赖类型

      Dependency.scala 源码有。

      Spark原理——逻辑执行图,Spark,大数据,spark,大数据

      • 一对一窄依赖

        其实 RDD 中默认的是 OneToOneDependency, 后被不同的 RDD 子类指定为其它的依赖类型, 常见的一对一依赖是 map 算子所产生的依赖, 例如 rddB = rddA.map(…)

        Spark原理——逻辑执行图,Spark,大数据,spark,大数据

        • 每个分区之间一一对应, 所以叫做一对一窄依赖
      • Range 窄依赖

        Range 窄依赖其实也是一对一窄依赖, 但是保留了中间的分隔信息, 可以通过某个分区获取其父分区, 目前只有一个算子生成这种窄依赖, 就是 union 算子, 例如 rddC = rddA.union(rddB)

        Spark原理——逻辑执行图,Spark,大数据,spark,大数据

        • rddC 其实就是 rddA 拼接 rddB 生成的, 所以 rddC 的 p5 和 p6 就是 rddB 的 p1 和 p2
        • 所以需要有方式获取到 rddC 的 p5 其父分区是谁, 于是就需要记录一下边界, 其它部分和一对一窄依赖一样
      • 多对一窄依赖

        多对一窄依赖其图形和 Shuffle 依赖非常相似, 所以在遇到的时候, 要注意其 RDD 之间是否有 Shuffle 过程, 比较容易让人困惑, 常见的多对一依赖就是重分区算子 coalesce, 例如 rddB = rddA.coalesce(2, shuffle = false), 但同时也要注意, 如果 shuffle = true 那就是完全不同的情况了

        Spark原理——逻辑执行图,Spark,大数据,spark,大数据

        • 因为没有 Shuffle, 所以这是一个窄依赖
      • 再谈宽窄依赖的区别文章来源地址https://www.toymoban.com/news/detail-790015.html

        • 宽窄依赖的区别非常重要, 因为涉及了一件非常重要的事情: 如何计算 RDD ?
        • 宽窄依赖的核心区别是: 窄依赖的 RDD 可以放在一个 Task 中运行

到了这里,关于Spark原理——逻辑执行图的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 大数据技术原理与应用 实验6 Spark数据处理系统的搭建

    熟悉常用的Spark操作。 1.熟悉Spark Shell的使用; 2.熟悉常用的Spark RDD API、Spark SQL API和Spark DataFrames API。 操作系统:Linux Spark版本: 1.6 Hadoop版本: 3.3.0 JDK版本:1.8 使用Spark shell完成如下习题: a)读取Spark安装目录下的文件README.md(/usr/local/spark/README.md); b)统计包含“Spark”的单词

    2024年02月09日
    浏览(62)
  • 大数据经典技术解析:Hadoop+Spark大数据分析原理与实践

    作者:禅与计算机程序设计艺术 大数据时代已经来临。随着互联网、移动互联网、物联网等新兴技术的出现,海量数据开始涌现。而在这些海量数据的基础上进行有效的处理,成为迫切需要解决的问题之一。Apache Hadoop和Apache Spark是目前主流开源大数据框架。由于其易于部署

    2024年02月07日
    浏览(43)
  • 大数据技术原理及应用课实验7 :Spark初级编程实践

    实验7  Spark初级编程实践 一、实验目的 1. 掌握使用Spark访问本地文件和HDFS文件的方法 2. 掌握Spark应用程序的编写、编译和运行方法 二、实验平台 1. 操作系统:Ubuntu18.04(或Ubuntu16.04); 2. Spark版本:2.4.0; 3. Hadoop版本:3.1.3。 三、实验步骤(每个步骤下均需有运行截图) 实

    2024年01月22日
    浏览(52)
  • Spark-机器学习(4)回归学习之逻辑回归

    在之前的文章中,我们来学习我们回归中的线性回归,了解了它的算法,知道了它的用法,并带来了简单案例。想了解的朋友可以查看这篇文章。同时,希望我的文章能帮助到你,如果觉得我的文章写的不错,请留下你宝贵的点赞,谢谢。 Spark-机器学习(3)回归学习之线性

    2024年04月25日
    浏览(72)
  • Apache Doris (二十八):Doris 数据导入(六)Spark Load 1- 原理及配置

    目录 1. 基本原理  2. Spark集群搭建 2.1 Spark Standalone 集群搭建 2.2 Spark On Yarn 配置

    2024年02月16日
    浏览(46)
  • 【基于IDEA + Spark 3.4.1 + sbt 1.9.3 + Spark MLlib 构建逻辑回归鸢尾花分类预测模型】

    基于IDEA + Spark 3.4.1 + sbt 1.9.3 + Spark MLlib 构建逻辑回归鸢尾花分类预测模型,这是一个分类模型案例,通过该案例,可以快速了解Spark MLlib分类预测模型的使用方法。 运行结果如下:

    2024年02月14日
    浏览(45)
  • spark sql 执行流程

    最近学习了spark sql执行流程,从网上搜到了大都是sql解析、analyzer、optimizer阶段、sparkplan阶段,但是我比较好奇的是,这几个阶段是怎么串起来的,于是花了好几天着重从源码层面看看了看具体实现,写了几点自己认为应该注意的地方。 spark sql的命令主要分为两种形式:com

    2024年02月14日
    浏览(37)
  • Spark任务执行流程

    Driver启动,创建SparkContext SparkContext 向资源管理器注册,并向资源管理器申请运行 Executor 资源管理器分配资源,然后资源管理器启动 Executor Executor 发送心跳至资源管理器 Executor 向 Driver注册自己 SparkContext 构建 DAG 有向无环图 DAGScheduler 将 DAG 分解成 Stage(TaskSet) 把 Stage 发送

    2024年02月06日
    浏览(51)
  • Spark 作业执行流程

    Spark的基本组件,包括负责集群运行的Master和Worker,负责作业运行的Client和Driver,以及负责集群资源管理器(如YARN)和执行单元Executor等。 从架构层面上来说,每一个Spark Application都由控制集群的主控节点Master、负责集群资源管理的Cluster Manager、执行具体任务的Worker节点和执

    2024年02月09日
    浏览(34)
  • Spark DataFrame:从底层逻辑到应用场景的深入解析

    本文将深入探讨Spark DataFrame的底层逻辑、发展由来以及应用场景。通过了解DataFrame的底层逻辑,我们可以更好地理解其在Spark中的重要地位。同时,了解DataFrame的发展历程和应用场景,可以帮助我们更好地掌握这一强大的数据处理工具,并在实际工作中加以运用。 DataFrame的底

    2024年01月24日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包