从物理执行的角度透视Spark Job

从物理执行的角度透视Spark Job

视频学习来源:DT-大数据梦工厂 IMF传奇行动视频(后附王家林老师联系方式)


本期内容:
1 再次思考pipeline
2 窄依赖物理执行内幕
3 宽依赖物理执行内幕
4 Job提交流程

思考一:pipeline(计算两种方式)
         即使采用pipeline的方式,函数对依赖的RDD中的数据的操作也会有两种方式:
         1、f(record),f作用于集合的每一条记录,每次只作用于一条记录;
         2、f(record),f一次性作用于全部数据;
Spark采用第一种方式,原因:
         1、无需等待,最大化使用集群的资源;
         2、减少OOM的发生;
         3、最大化的有利于并发;
         4、可以精准控制每一个Partition本身(Dependency)及其内部的计算(最重要);
         5、基于lineage的算子流动式函数式编程,减少中间结果的产生,并且可以最快的恢复;
疑问:会不会增加网络通信,答案是不会,因为在pipeline
思考二:思考Spark Job的物理式执行
         Spark Application里边可以产生一个或者多个Job,例如Spark-shell默认启动的时候没有job,只是作为资源的分片程序,可以在里面写代码会产生若干个job,普通程序中一般会有不同的Action,每一个Action一般也会产生一个Job(也有情况是Action会产生其他Action)。
          Spark是MapReduce思想的更加精致的实现,MapReduce有很多具体不同的实现,例如Hadoop的MapReduce,基本的计算流程如下:
   首先并发,以JVM为对象的并发执行的mapper,mapper的map会产生输出数据,输出数据会根据Partition指定的规则放到Local FileSystem,然后在经由Shuffle、Sort、Aggregate变成Reducer中的reduce的输出,执行reduce产生的输出结果,Hadoop MapReduce执行的流程若然简单,但是笔记死板,尤其在构造复杂算法的时候,非常不利于算法的实现,且执行效率极为低下!
         Spark执行时物理算法构造和物理执行时最最基本的核心最大化pipeline,pipeline愈多数据覆用越好,速度非常快,也就是数据被使用的时候再进行计算。     
          从数据流动的视角来说,是数据流动到计算的位置!
          实质上从逻辑的角度看是算子在数据上流动;
         从算法构建的角度而言,肯定是算子作用于数据,所以是算子在数据上流动,方便算法的构建;
         从物理执行角度而言,是数据流动到计算的位置,有利于最高效的计算,方便系统最高效的运行。
         对于pipeline而言,数据计算的位置就是每个Stage最后的RDD,前面的RDD都是最后一个步骤进行函数式展开的,即可以理解为一个函数。
内幕:每个Stage中除了最后一个RDD的算子是真实的以外,前面的算子都是假的。原因有下面两点:
         1、从第5000个步骤要结果,然后进行算子合并,从后往前推进行函数式展开,进而产生链条;
         2、JVM不知道什么是RDD;
         由于计算的lazy特性,导致计算 从后往前回溯,形成Computing Chain,导致的结果就是需要首先计算出一个Stage内部左侧的RDD的本次计算依赖的Partition(实际计算是从前往后的),

第三点:窄依赖的物理执行内幕
         一个Stage内部的RDD都是窄依赖,窄依赖计算本身是逻辑上看是从Stage内部最左侧的RDD开始立即计算的,根据Computing Chain,数据(Record)从一个计算步骤流动到下一个计算步骤,以此类推,知道计算到Stage内部的最后一个RDD来产生计算结果。
         Computing Chian的构建是从后往前回溯构建而成的,而实际的物理计算则是让数据从前往后在算子上流动的,知道流动到不能在进行流动位置才开始计算下一个Record,这就导致一个美好的结果: 后面的RDD对前面的RDD的依赖虽然是Partition级别的数据集合的依赖,但是并不需要父RDDPartition中所有的Records计算完毕才整体往后流动数据进行计算,这就极大的提高了计算速率!
/**
 * An RDD that applies the provided function to every partition of the parent RDD.
 */
private[spark] class MapPartitionsRDD[U: ClassTag, T: ClassTag](
    prev: RDD[T],
    f: (TaskContext, Int, Iterator[T]) => Iterator[U],  // (TaskContext, partition index, iterator)
    preservesPartitioning: Boolean = false)
  extends RDD[U](prev) {
  override val partitioner = if (preservesPartitioning) firstParent[T].partitioner else None
  override def getPartitions: Array[Partition] = firstParent[T].partitions
  override def compute(split: Partition, context: TaskContext): Iterator[U] =
    f(context, split.index, firstParent[T].iterator(split, context))
第四点:宽依赖物理执行内幕
  注:  必须等到依赖的父Stage中的最后一个RDD把全部数据彻底计算完毕,才能过经过Shuffle来计算当前的Stage(我们在写代码的时候尽量减少宽依赖)

 /**
   * Return the number of elements in the RDD.
   */
  def count(): Long = sc.runJob(this, Utils.getIteratorSize _).sum
  /**
   * Approximate version of count() that returns a potentially incomplete result
   * within a timeout, even if not all tasks have finished.
   */
  def countApprox(
      timeout: Long,
      confidence: Double = 0.95): PartialResult[BoundedDouble] = withScope {
    val countElements: (TaskContext, Iterator[T]) => Long = { (ctx, iter) =>
      var result = 0L
      while (iter.hasNext) {
        result += 1L
        iter.next()
      }
      result
    }
    val evaluator = new CountEvaluator(partitions.length, confidence)
    sc.runApproximateJob(this, countElements, evaluator, timeout)
  }


王家林老师是大数据技术集大成者,中国Spark第一人:

DT大数据梦工厂

新浪微博:www.weibo.com/ilovepains/

微信公众号:DT_Spark

博客:http://.blog.sina.com.cn/ilovepains

TEL:18610086859

Email:18610086859@vip.126.com







































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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值