Spark源码解读之Stage划分和提交

Spark_Spark中 Stage, Job 划分依据 , Job, Stage, Task 高阶知识 Task内部有一个成员变量:preferredLocations表明了这个Task的位置偏好,这个变量的值是根据Task的数据的位置得到的,可以是一个hostName或者execotorId。例如,如果Task的数据是在192.168.5.101和192.168.5.102这两台机器上,那么:forExecutor是一个HashMap,key为executorId,value是preferredLocations为这个executor的所有task的taskId。 阅读详情

上一篇讲解了Spark源码解读之Job提交,这一篇主要讲解Stage划分和提交。

调用流程:

org.apache.spark.scheduler.DAGScheduler.handleJobSubmitted

org.apache.spark.scheduler.DAGScheduler.submitStage

org.apache.spark.scheduler.DAGScheduler.submitMissingTasks

org.apache.spark.scheduler.TaskScheduler.submitTasks


一、Stage划分

Spark中会根据RDD之间的依赖关系进行Stage划分,在遇到ShuffleDependency时,会将这两个RDD划分到不同的Stage。在调用DAGScheduler的handleJobSubmitted进行Job提交后,会先进行Stage划分,源码如下:

// 参数finalRDD为触发action操作时最后一个RDD
private[scheduler] def handleJobSubmitted(jobId: Int,
    finalRDD: RDD[_],
    func: (TaskContext, Iterator[_]) => _,
    partitions: Array[Int],
    callSite: CallSite,
    listener: JobListener,
    properties: Properties) {
  var finalStage: ResultStage = null
  try {
    // New stage creation may throw an exception if, for example, jobs are run on a
    // HadoopRDD whose underlying HDFS files have been deleted.
    // 创建finalStage
    finalStage = newResultStage(finalRDD, func, partitions, jobId, callSite)
  } catch {
    case e: Exception =>
      logWarning("Creating new stage failed due to exception - job: " + jobId, e)
      listener.jobFailed(e)
      return
  }

  val job = new ActiveJob(jobId, finalStage, callSite, listener, properties)
  clearCacheLocs()
  logInfo("Got job %s (%s) with %d output partitions".format(
    job.jobId, callSite.shortForm, partitions.length))
  logInfo("Final stage: " + finalStage + " (" + finalStage.name + ")")
  logInfo("Parents of final stage: " + finalStage.parents)
  logInfo("Missing parents: " + getMissingParentStages(finalStage))

  val jobSubmissionTime = clock.getTimeMillis()
  jobIdToActiveJob(jobId) = job
  activeJobs += job
  finalStage.setActiveJob(job)
  val stageIds = jobIdToStageIds(jobId).toArray
  val stageInfos = stageIds.flatMap(id => stageIdToStage.get(id).map(_.latestInfo))
  listenerBus.post(
    SparkListenerJobStart(job.jobId, jobSubmissionTime, stageInfos, properties))
  // 提交finalStage,该方法会提交所有关联的未提交的stage
  submitStage(finalStage)

  submitWaitingStages()
}

可以看出,在创建finalStage时初始化了newResultStage实例。最后调用submitStage方法(详情见Stage提交部分)。newResultStage源码如下:

/**
 * Create a ResultStage associated with the provided jobId.
 */
private def newResultStage(
    rdd: RDD[_],
    func: (TaskContext, Iterator[_]) => _,
    partitions: Array[Int],
    jobId: Int,
    callSite: CallSite): ResultStage = {
  // 获取parent Stages和id
  val (parentStages: List[Stage], id: Int) = getParentStagesAndId(rdd, jobId)
  // 创建stage
  val stage = new ResultStage(id, rdd, func, partitions, parentStages, jobId, callSite)
  // 更新stageId和Stage、JobId和stageId之间的映射关系
  stageIdToStage(id) = stage
  updateJobIdStageIdMaps(jobId, stage)
  // 返回stage
  stage
}

初始化newResultStage实例时会做两件事,一是调用getParentStagesAndId方法得到parentStages和id,二是更新stageId和Stage、JobId和stageId之间的映射关系。下面是getParentStagesAndId源码:

/**
 * Helper function to eliminate some code re-use when creating new stages.
 */
private def getParentStagesAndId(rdd: RDD[_], firstJobId: Int): (List[Stage], Int) = {
  // 获取parentStages
  val parentStages = getParentStages(rdd, firstJobId)
  // 获取一个唯一id
  val id = nextStageId.getAndIncrement()
  (parentStages, id)
}

getParentStagesAndId会做两件事,一是调用getParentStages得到parentStages列表,二是获取一个Stage的唯一id。getParentStages源码如下:

/**
 * Get or create the list of parent stages for a given RDD.  The new Stages will be created with
 * the provided firstJobId.
 */
private def getParentStages(rdd: RDD[_], firstJobId: Int): List[Stage] = {
  val parents = new HashSet[Stage]
  val visited = new HashSet[RDD[_]]
  // We are manually maintaining a stack here to prevent StackOverflowError
  // caused by recursively visiting
  val waitingForVisit = new Stack[RDD[_]]
  // 广度优先遍历方式
  def visit(r: RDD[_]) {
    if (!visited(r)) { // visited为空
      visited += r
      // Kind of ugly: need to register RDDs with the cache here since
      // we can't do it in its constructor because # of partitions is unknown
      for (dep <- r.dependencies) {
        dep match {
        	// 依赖为ShuffleDependency类型时,则生成一个新的shuffle map Stage
          case shufDep: ShuffleDependency[_, _, _] =>
            parents += getShuffleMapStage(shufDep, firstJobId)
          // 依赖为非ShuffleDependency类型时,则加入到waitingForVisit栈中
          case _ =>
            waitingForVisit.push(dep.rdd)
        }
      }
    }
  }
  waitingForVisit.push(rdd)
  
  while (waitingForVisit.nonEmpty) {
  	// 调用visit方法
    visit(waitingForVisit.pop())
  }
  parents.toList
}

getParentStages会先创建一个类型为RDD的栈waitingForVisit,然后遍历waitingForVisit,如果该RDD的依赖为ShuffleDependency类型,则调用getShuffleMapStage方法得到一个shuffle map stage,否则将该RDD的父RDD加入到waitingForVisit中。getShuffleMapStage源码如下:

/**
 * Get or create a shuffle map stage for the given shuffle dependency's map side.
 */
private def getShuffleMapStage(
    shuffleDep: ShuffleDependency[_, _, _],
    firstJobId: Int): ShuffleMapStage = {
  shuffleToMapStage.get(shuffleDep.shuffleId) match {
    case Some(stage) => stage
    case None =>
      // We are going to register ancestor shuffle dependencies
      getAncestorShuffleDependencies(shuffleDep.rdd).foreach { dep =>
        shuffleToMapStage(dep.shuffleId) = newOrUsedShuffleStage(dep, firstJobId)
      }
      // Then register current shuffleDep
      val stage = newOrUsedShuffleStage(shuffleDep, firstJobId)
      shuffleToMapStage(shuffleDep.shuffleId) = stage
      stage
  }
}

getShuffleMapStage会获取或者创建一个shuffle map stage。


二、Stage提交

Stage划分完成后,会进行Stage提交,Stage提交首先会调用submitStage方法,源码如下:

/** Submits stage, but first recursively submits any missing parents. */
private def submitStage(stage: Stage) {
  val jobId = activeJobForStage(stage)
  if (jobId.isDefined) {
    logDebug("submitStage(" + stage + ")")
    if (!waitingStages(stage) && !runningStages(stage) && !failedStages(stage)) {
    	// 获取未提交的父Stage
      val missing = getMissingParentStages(stage).sortBy(_.id)
      logDebug("missing: " + missing)
      if (missing.isEmpty) { // 所有的父Stage都已提交
        logInfo("Submitting " + stage + " (" + stage.rdd + "), which has no missing parents")
        submitMissingTasks(stage, jobId.get) // 提交该Stage
      } else {// 父Stage会提交
        for (parent <- missing) {
          submitStage(parent) /// 提交父Stage
        }
        waitingStages += stage
      }
    }
  } else {
    abortStage(stage, "No active job for stage " + stage.id, None)
  }
}

submitStage会检测该Stage的父Stage是否提交,如果有父Stage未提交,则会递归调用submitStage;如果父Stage都已提交,则会调用submitMissingTasks方法提交该Stage。submitMissingTasks源码如下:

  /** Called when stage's parents are available and we can now do its task. */
  private def submitMissingTasks(stage: Stage, jobId: Int) {
    logDebug("submitMissingTasks(" + stage + ")")
    // Get our pending tasks and remember them in our pendingTasks entry
    stage.pendingPartitions.clear()

    // First figure out the indexes of partition ids to compute.
    // 得到需要计算的partitions
    val partitionsToCompute: Seq[Int] = stage.findMissingPartitions()

    // Create internal accumulators if the stage has no accumulators initialized.
    // Reset internal accumulators only if this stage is not partially submitted
    // Otherwise, we may override existing accumulator values from some tasks
    if (stage.internalAccumulators.isEmpty || stage.numPartitions == partitionsToCompute.size) {
      stage.resetInternalAccumulators()
    }

    // Use the scheduling pool, job group, description, etc. from an ActiveJob associated
    // with this Stage
    val properties = jobIdToActiveJob(jobId).properties

    runningStages += stage
    // SparkListenerStageSubmitted should be posted before testing whether tasks are
    // serializable. If tasks are not serializable, a SparkListenerStageCompleted event
    // will be posted, which should always come after a corresponding SparkListenerStageSubmitted
    // event.
    
    stage match {
      case s: ShuffleMapStage =>
        outputCommitCoordinator.stageStart(stage = s.id, maxPartitionId = s.numPartitions - 1)
      case s: ResultStage =>
        outputCommitCoordinator.stageStart(
          stage = s.id, maxPartitionId = s.rdd.partitions.length - 1)
    }
    
    // 创建一个Map:taskIdToLocations,存储的是id->Seq[TaskLocation]的映射关系,这里的id表示task所包含的RDD的partition id,TaskLocation表示任务位置
    // 实现时,对stage中需要计算的RDD的分区调用PreferredLocations来获取优先位置信息,映射成id->Seq[TaskLocation]的关系  
    val taskIdToLocations: Map[Int, Seq[TaskLocation]] = try {
      stage match {
        case s: ShuffleMapStage =>
          partitionsToCompute.map { id => (id, getPreferredLocs(stage.rdd, id))}.toMap
        case s: ResultStage =>
          val job = s.activeJob.get
          partitionsToCompute.map { id =>
            val p = s.partitions(id)
            (id, getPreferredLocs(stage.rdd, p))
          }.toMap
      }
    } catch {
      case NonFatal(e) =>
        stage.makeNewStageAttempt(partitionsToCompute.size)
        listenerBus.post(SparkListenerStageSubmitted(stage.latestInfo, properties))
        abortStage(stage, s"Task creation failed: $e\n${e.getStackTraceString}", Some(e))
        runningStages -= stage
        return
    }

    stage.makeNewStageAttempt(partitionsToCompute.size, taskIdToLocations.values.toSeq)
    listenerBus.post(SparkListenerStageSubmitted(stage.latestInfo, properties))

    // TODO: Maybe we can keep the taskBinary in Stage to avoid serializing it multiple times.
    // Broadcasted binary for the task, used to dispatch tasks to executors. Note that we broadcast
    // the serialized copy of the RDD and for each task we will deserialize it, which means each
    // task gets a different copy of the RDD. This provides stronger isolation between tasks that
    // might modify state of objects referenced in their closures. This is necessary in Hadoop
    // where the JobConf/Configuration object is not thread-safe.
    var taskBinary: Broadcast[Array[Byte]] = null
    try {
      // For ShuffleMapTask, serialize and broadcast (rdd, shuffleDep).
      // 对于ShuffleMapTask,序列化并广播,广播的是rdd和shuffleDep  
      // For ResultTask, serialize and broadcast (rdd, func).
      // 对于ResultTask,序列化并广播,广播的是rdd和func
      val taskBinaryBytes: Array[Byte] = stage match {
        case stage: ShuffleMapStage =>
          closureSerializer.serialize((stage.rdd, stage.shuffleDep): AnyRef).array()
        case stage: ResultStage =>
          closureSerializer.serialize((stage.rdd, stage.func): AnyRef).array()
      }

      taskBinary = sc.broadcast(taskBinaryBytes)
    } catch {
      // In the case of a failure during serialization, abort the stage.
      case e: NotSerializableException =>
        abortStage(stage, "Task not serializable: " + e.toString, Some(e))
        runningStages -= stage

        // Abort execution
        return
      case NonFatal(e) =>
        abortStage(stage, s"Task serialization failed: $e\n${e.getStackTraceString}", Some(e))
        runningStages -= stage
        return
    }
		
		// 针对stage的每个分区构造task,形成tasks:ShuffleMapStage生成ShuffleMapTasks,ResultStage生成ResultTasks  
    val tasks: Seq[Task[_]] = try {
      stage match {
        case stage: ShuffleMapStage =>
          partitionsToCompute.map { id =>
            val locs = taskIdToLocations(id)
            val part = stage.rdd.partitions(id)
            new ShuffleMapTask(stage.id, stage.latestInfo.attemptId,
              taskBinary, part, locs, stage.internalAccumulators)
          }

        case stage: ResultStage =>
          val job = stage.activeJob.get
          partitionsToCompute.map { id =>
            val p: Int = stage.partitions(id)
            val part = stage.rdd.partitions(p)
            val locs = taskIdToLocations(id)
            new ResultTask(stage.id, stage.latestInfo.attemptId,
              taskBinary, part, locs, id, stage.internalAccumulators)
          }
      }
    } catch {
      case NonFatal(e) =>
        abortStage(stage, s"Task creation failed: $e\n${e.getStackTraceString}", Some(e))
        runningStages -= stage
        return
    }
		
    // 如果存在tasks,则利用taskScheduler.submitTasks()提交task,否则标记stage已完成  
    if (tasks.size > 0) {
      logInfo("Submitting " + tasks.size + " missing tasks from " + stage + " (" + stage.rdd + ")")
      stage.pendingPartitions ++= tasks.map(_.partitionId)
      logDebug("New pending partitions: " + stage.pendingPartitions)
      // 调用taskScheduler.submitTasks提交task
      taskScheduler.submitTasks(new TaskSet(
        tasks.toArray, stage.id, stage.latestInfo.attemptId, jobId, properties))
      // 记录提交时间
      stage.latestInfo.submissionTime = Some(clock.getTimeMillis())
    } else {
      // Because we posted SparkListenerStageSubmitted earlier, we should mark
      // the stage as completed here in case there are no tasks to run
      markStageAsFinished(stage, None)

      val debugString = stage match {
        case stage: ShuffleMapStage =>
          s"Stage ${stage} is actually done; " +
            s"(available: ${stage.isAvailable}," +
            s"available outputs: ${stage.numAvailableOutputs}," +
            s"partitions: ${stage.numPartitions})"
        case stage : ResultStage =>
          s"Stage ${stage} is actually done; (partitions: ${stage.numPartitions})"
      }
      logDebug(debugString)
    }
  }

submitMissingTasks会做以下几个事:

1. 清空stage的pendingPartitions

2. 得到需要计算的partition id索引,放入partitionsToCompute

3. 将stage加入到runningStages中 

4. 启动一个stage

5. 得到task中执行的位置,即计算stage的每个RDD的partition的优先位置,存入taskIdToLocations

6. 对stage进行序列化并广播

7. (重要)针对stage的每个RDD的partition构造task,存入tasks

8. 存在tasks,则调用taskScheduler.submitTasks()提交task,否则标记stage已完成。

Spark-Core源码学习记录 4 Stage划分以及Task本地性计算与分发 Spark-Core源码学习记录 该系列作为Spark源码回顾学习的记录,旨在捋清Spark分发程序运行的机制和流程,对部分关键源码进行追踪,争取做到知其所以然,对枝节部分源码仅进行文字说明,不深入下钻,避免混淆主干内容。 前面篇章中,我们完成了Master与Worker的注册启动,Driver和Executor的注册启动,Application的注册与启动。初始化了SparkContext、Sc... 阅读详情

相关推荐

Spark由宽依赖创建Stage过程源码解读

我们来探讨一下Spark创建Stage的源码过程。 首先,下面我提供一个很简单的测试。我们将一个List经过了两次map和一次reduceByKey后的RDD用collect执行。 object TransTest { def main(args: Array[String]): Unit = { val conf = new SparkConf().setMaster("local").setAppName("TransTest") val sparkContext = new Spa

weixin_41812379的博客 468

Spark:Stage介绍

spark job是根据action算子触发的,遇到action算子就会起一个job stage的划分依据就是看是否产生了shuflle(即宽依赖),遇到一个shuffle操作就划分为前后两 个stage stage是由一组并行的task组成,stage会将一批task用TaskSet来封装,提交给TaskScheduler进行分配,最后发送到Executor执行 下面来看一张图来具体分析一下 为什么是从后往前呢? 因为RDD之间是有血缘关系的,后面的RDD依赖前面的RDD,也就是说后面的RDD要等前

简单随风的博客 3623

Spark源码解读(6)——Shuffle过程

Shuffle应该说是Spark Core中较为复杂的部分,本文主要从一个最简单的WordCount例子出发分析Spark的Shuffle过程: sc.parallelize(1 to 1000).map(i=>(i%5,1)).reduceByKey(_+_).collect() 计算过程中会分成两个Stage如下图所示: 每个Stage由多个Task组成,同一Stage的各Task并

scalahome的博客 2455

Spark之Stage的生成及任务的执行

每一个Spark应用都会创建一个sparksession,用来跟Spark集群交互,如果提交任务的模式为cluster模式,则Driver进程会被随机在某个worker结点上启动,然后真正执行用户提供的入口类,或是使用Spark内置的入口类,同时在Driver中会创建 clientmasterworkerdriverdirverexecutorRequestSubmitDriverLaunchDrivernew DriverRunner["deploy.worker.DriverWrapper"]Proce

Dreamming Time的博客 2753

拉曼光谱仪的原理与应用——TFN 7G F660手持式拉曼光谱识别仪

拉曼光谱仪的原理与应用——TFN 7G F660手持式拉曼光谱识别仪

TFN_yzgd的博客 926

Unity 常见问题 之 打包编译的报错 Unable to sign the application; please provide passwords!

Unity 常见问题 之 打包编译的报错 Unable to sign the application; please provide passwords! 目录 Unity 常见问题 之 打包编译的报错 Unable to sign the application; please provide passwords! 一、简单介绍 二、问题描述 三、原因分析 四、可能的解决方法 方法一:取消 Keystore 设置即可 方法二:重新添加 Keystore 相关设置 一...

仙魁XAN 1万+

Spark中的Stage概念

Spark中的Stage概念

qq_42952637的博客 1979

A Mathematical Framework for Transformer Circuits—(二)

transformer数学理论框架

qq_42208244的博客 757

Spark Stage的划分

RDD之间有一系列的依赖关系,依赖关系又分为窄依赖和宽依赖。Spark中的Stage其实就是一组并行的任务,任务是一个个的task 。 窄依赖 父RDD和子RDD partition之间的关系是一对一的。或者父RDD一个partition只对应一个子RDD的partition情况下的父RDD和子RDD partition关系是多对一的。不会有shuffle的产生。父RDD的一个分区去到子RDD的...

精益求精 2万+

解锁激光加工密码:功率、频率、脉宽、占空比全解析

激光加工利用高能量激光束实现材料加工,具有无接触、高效率等特点。其核心参数包括功率(决定能量输出)、频率(控制脉冲重复)、脉宽(调节能量作用时间)和占空比(调控工作周期),这些参数相互协同影响加工效果。针对不同材料需采用差异化参数组合:金属材料需较高功率,塑料需低功率防过热,陶瓷需高频短脉宽减少热应力。实际操作中应通过小范围测试逐步优化参数,并注意解决烧损、切割不平整等常见问题。合理调节这些参数可实现高质量、高效率的激光加工。

远程部署调试 运行安装 项目调试 二次开发 项目技术新 持续迭代 部分源码免费分享 614

第二篇:APS智能排产实战:OR-Tools与coptpy从入门到案例

专注于具体的技术实现,包括 OR-Tools 和 coptpy 的详细使用指南、建模技巧、日志解读,以及一个完整的两阶段生产排程案例。适合准备动手开发 APS 系统的工程师和研究者。

算法工程师 452

Spark的Sate划分源码剖析

Spark的Sate划分源码剖析 1.主程序Driver program 需要初始化SparkContext 2.在初始化时会创建三个重量级的类 ​ 1.创建schedulerBacked 负责连接后端计算资源 ​ 2.创建taskScheduler 负责阶段任务调度 ​ 3.创建DAGScheduler 负责阶段划分 // Create and start the scheduler //初始...

Java学习笔记 416

RK3588 平台imx334 驱动调试记录

rk3588调试imx334

xiaoning132的专栏 1789

hadoop源码分析_Spark2.x精通:Job触发流程源码深度剖析(一)

, 一、概述之前几篇文章对Spark集群的Master、Worker启动流程进行了源码剖析,后面直接从客户端角度出发,讲解了spark-submit任务提交过程及driver的启动;集群启动、任务提交、SparkContext初始化等前期准备工作完成之后,后面就是我们的主函数的代码Job如何触发的,本篇文章还是结合源码进行剖析。软件版本:sp...

weixin_39603613的博客 167

【matlab】基于 Simulink 的空调恒温控制系统仿真建模

本文介绍了基于Simulink的空调恒温控制系统仿真建模方法。系统采用PID控制算法,通过传感器测量温度、控制器计算信号、执行器调节功率来实现恒温控制。文章详细阐述了Simulink模型搭建步骤,包括PID控制器、房间热力学特性模拟等模块的实现,并提供了测试流程和多种实际部署场景。该方法可有效验证控制算法性能,优化系统设计,适用于家用、商用、工业及汽车空调系统。未来可结合智能算法和节能优化技术进一步提升系统性能。

走向CTO的路上... 917

基于matlab的CNN手写数字图像矫正系统

CNN 是一款强大的图像特征抽象和提取工具,常用于图像分类识别,并取得了较为理想的效果。我们通过 MATLAB 可以方便地设计不同结构、不同深度的 CNN 进行图像分类,最后一层一般设置为分类层(ClassificationLayer)进行类别标签的输出。如果我们将最后一层设计为回归层(RegressionLayer),则可以将其应用于对连续数据的预测,这对于曲线拟合、预测分析都有重要的意义。% 自定义 CNN 回归网络结构layers = [% 输入% 卷积% 池化% 池化% 卷积。

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 674

spark rdd 做判断_深入分析Spark任务调度的原理Java后端同学入门Spark编程系列

作者:陌北有棵树,Java人,架构师社区合伙人!之前写了一篇:《我作为Java后端,分享一下入门Spark编程的经历!》上篇是Spark入门的第一篇,写了一些关于Spark编程中RDD的一些基本操作,主要是为了能快速入手Spark编程,接下来会对Spark的内部原理进行分析。对于Spark来说,任务的调度和执行可以说是其运行的核心流程,所以本文从源码的角度对这个过程进行详细的分析。【一】...

weixin_35045323的博客 238

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

Spark源码走读4——Scheduler

用户提交的Job到DAGScheduler后,会封装成ActiveJob,同时启动JobWaiter监听作业的完成情况。同时依据job中RDD的dependency和dependency属性(NarrowDependency,ShufflerDependecy),DAGScheduler会根据依赖关系的先后产生出不同的stage DAG(result stage, shuffle map ...

weixin_30902251的博客 211

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

[源码] Spark如何划分Stage

[源码] Spark如何划分Stage 文章目录[源码] Spark如何划分Stage大家好,我是一拳就能打爆你A柱的猛男1、Stage的介绍1.1 Stage是什么概念以及Stage的划分1.3 Stage的意义2、从SparkPi定位Stage划分代码3、Spark划分Stage代码解读4、总结 大家好,我是一拳就能打爆你A柱的猛男 太久不写博客了,今天写的博客可能有点生疏,各位将就看一下。不知道有没有人跟我一样好奇Spark如何划分Stage的,今天翻一下源码给大家看看。 1、Stage的介绍 这部分

Alian_W的博客 2783

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

90%的Spark开发者没搞懂的Stage划分细节?逐行拆解,从源码中找答案

Stage的划分过程中,会涉及到宽依赖和窄依赖的概念,宽依赖是Stage的分界线,连续的窄依赖都属于同一Stage。

涤生大数据 1315

spark源码解读-stage,task等划分

如何划分stage def dataAnalysis() = { val lines = wordCountDao.readFile("datas/word.txt") val words: RDD[String] = lines.flatMap(_.split(" ")) val wordToOne = words.map(word=>(word,1)) val wordToSum: RDD[(String, Int)] = wordToOne.reduceByKey(_+_)

俩只猴的博客 481

spark源码跟踪(四)stage提交与task任务生成

stage提交与task任务生成一,程序入口。二,stage提交2.1,2.2,三,Task任务生成四,总论 链接: spark源码跟踪(三)stage划分. 一,程序入口。 val sparkConnf=new SparkConf().setAppName("wordCount").setMaster("local[3]") val sparkContext=new SparkContext(sparkConnf) val rdd=sparkContext.parallelize(Array("hell

weixin_43172032的博客 1365

Spark源码解读--任务生成和提交过程

Application 把APP打包上传到集群: $SPARK_HOME/spark-submit \ –class com.sparktest \ –master spark://minimaster:7077 \ /home/wc.jarDriver端: 1.调用SparkSubmit类,内部执行submit → doRunMain → 通过反射获取应用

长胖的wo一定特美的博客 743

Spark源码解读之Job提交

上一篇讲解了Spark源码解读之RDD构建和转换过程,上一篇的RDD操作都是transform,也就是说结果会是一个新的RDD,并不会进行真正地计算,真正会引发Spark计算操作时action。比如first、count、collect。 WordCounts.collect()会真正出发一个Job的执行。RDD的collect源码如下: // 返回RDD所包含的所有元素 def

Oner.wv的专栏 1308

【原】Spark中Stage的提交源码解读

版权声明:本文为原创文章,未经允许不得转载。 复习内容: Spark中Job如何划分为Stage http://www.cnblogs.com/yourarebest/p/5342424.html 1.Spark中Stage的提交 1.在复习内容中,将Job划分为Stage这一过程的调用起始于方法handleJobSubmitted,同样Stage的提交也包含在该方法中,如下所示: priva...

weixin_30645617的博客 180

Spark源码分析(一):从spark-submit开始到Task提交

Spark源码分析(一):spark-submit 1.提交作业时shell脚本中使用的spark-submit (1)spark-submit (2)spark-core中的org.apache.spark.deploy.SparkSubmit (3)执行submit(appArgs) (4)runMain方法通过反射调用main()方法 2....

weixin_42084234的博客 251
上一篇: Spark源码解读之Job提交
下一篇: Python爬取58足浴上网站信息
Wang_AI
博客等级 码龄12年 988粉丝 162原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值