Spark-core 行动算子(十)

本文详细介绍了Spark中的Action算子,包括reduce、count、collect、first、take、takeSample、takeOrdered、countByKey、saveAs*File和foreach。同时,讨论了Action算子与transformations算子的区别,以及RDD的持久化和存储级别选择,强调了Spark中RDD缓存的管理与使用。
摘要由CSDN通过智能技术生成
Action 算子详解


  上文我们讲过了常用的转换算子,本篇博客记录一下常用的行动算子,那我们开始啦。

在这里插入图片描述

一、Action算子和transformations算子的区别

  • transformations算子是惰性算子,也就是说它的触发条件不是代码执行到这,而是需要transformations算子的结果后才执行这个算子,transformations也为我们减少了很多非必要的开销。
  • Action算子是一个触发计算的算子,我们知道Spark是由很多的任务组成,而一个Action便形成了一个新的任务,也及时Task。可能有一个或者多个Task组成了我们的application。

既然都讲到这了,我们顺便讲一讲Spark的任务组成吧。前面说了可能会造成shuffle的算子,这里我们成为shuffle算子,切记shuffle算子和transformations算子、Action算子并不是一回事。每一个shuffle都将是一个stage,一个Task可能有一个或者多个stage组成,然后一个或者多个Task组成了我们的应用。
在这里插入图片描述这个是一个task的DAG(有向无环图),然后如下图,多个stage组成Task,很多的Task组成一个application。我们知道stage的一般形成条件是出现shuffle算子,而Task的形成条件是Action算子,我们可以随便打开一个行动算子源码,可以看到里面均有一行大致相同的代码,sc.runJob就是触发计算的触发器,当调用sc.runJob后才开始真正的计算。

sc.runJob(this, reducePartition, mergeResult)

在这里插入图片描述

二、Action算子

1、reduce

  reduce是Spark行动算子里的聚合算子,主要作用是对RDD做聚合作用,比如求和,极值之类的。相对也比较简单。那我们先看一下使用方法:

    // TODO 创建执行环境
    val conf = new SparkConf().setMaster("local[*]").setAppName("create")
    val sc = new SparkContext(conf)
    val rdd1: RDD[Int] = sc.makeRDD(List(1, 2, 4, 5, 6, 7, 8, 9))
    // 求和
    val i: Int = rdd1.reduce(_ + _) // output: 42
    // 求最大值
    val i1: Int = rdd1.reduce((x, y) => {
   
      if (x > y) {
   
        x
      }
      else
        y
    }) // output: 9

用法就是这样简单,要是想求平均的话,直接在和的基础上在除以元素个数即可。
那我们在看一下源码:

  def reduce(f: (T, T) => T): T = {
   
    val cleanF = sc.clean(f)
    val reducePartition: Iterator[T] => Option[T] = iter => {
   
      if (iter.hasNext) {
   
        Some(iter.reduceLeft(cleanF))   //一个一个迭代,从左开始
      } else {
   
        None
      }
    }
    var jobResult: Option[T] = None
    val mergeResult = (index: Int, taskResult: Option[T]) => {
     //当每个任务执行完后,都会返回一个这样的结果,index为分区的索引
      if (taskResult.isDefined) {
   
        jobResult = jobResult match {
   
          case Some(value) => Some(f(value, taskResult.get))   //第二个分区的结果与第一个分区的一起,再用f处理
          case None => taskResult  //当第一个任务完成时,会进入,taskResult 为第一个分区的结果
        }
      }
    }
    sc.runJob(this, reducePartition, mergeResult)   //任务的执行
    // Get the final result out of our Option, or throw an exception if the RDD was empty
    jobResult.getOrElse(throw new UnsupportedOperationException("empty collection"))
  }

通过源码,可以看到reduce先有reducePartition获取一个分区的结果的方法,然后再合并结果的方法,最后将他们一并传给 sc.runJob开始计算。有人疑问,为什么把Action的方法传入 sc.runJob就可以获取结果,前面提到有向无环图,当我们把action的方法给runJob,先去寻找该行动算子计算所需要的数据,接着再寻找这个数据的来源,又有人要问了,那怎么寻找了?看上图有向无环图,在Saprk内部维护了一个有向无环图,当我们需要这个数据时候,我们就开始一级一级的往上寻找,直至找到已存在的数据源,然后再往回一级一级的计算,直到获取最后的Action结果,有点绕,慢慢思考一下就可以了。

2、count

  count算子就是获取RDD中元素个数,用法简单,先看一下用法:

    val conf = new SparkConf().setMaster("local[*]").setAppName("create")
    val sc = new SparkContext(conf)
    val rdd1: RDD[Int] = sc.makeRDD(List(1, 2, 4, 5, 6, 7, 8, 9))
    val rdd: RDD[(String, Double)] = sc.makeRDD(List(("Tom", 88.0), ("Tom", 90.0), ("jack", 86.0), ("Toni", 85.0), ("jack", 82.0), ("Tom", 93.0)),2)
    val l: Long = rdd1.count() // 8

源码:


  /**
   * Return the number of elements in the RDD.
   */
  def count(): Long = sc.runJob(this, Utils.getIteratorSize _).sum

3、collect

  collect算子就是将RDD元素以scala的数据结构Array形式展示,可以直接打印:

    val conf = new SparkConf().setMaster("local[*]").setAppName("create")
    val sc = new SparkContext(conf)
    val rdd1: RDD[Int] = sc
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值