spark资源调度和任务调度

不问归期 提交于 2019-12-10 23:36:57

资源调度:

(1)executor默认在集群中分散启动,可通过参数配置集中在某个work启动,不过分散启动有利于数据本地化。

(2)如果spark-submit提交任务时,如果不指定--executor-cores,则spark会在每个work中启动一个executor并消耗掉work中的所有core和1G的内存。

(3)如果只设置--executor-cores而不设置--total-executor-cores则会,每启动一个executor耗费--executor-cores配置的核,而且也会消耗掉所有work中的core,直到不能在启动executor为止。

(4)只有指定--executor-cores并且指定--total-executor-cores,才会限定住executor的个数每个executor需要的core个数

任务调度:

按照action算子划分job,每个job由DAGSchedule划分stage,由finalRdd开始由后向前递归划分stage,划分stage的关键方法是submitStage:

 private def submitStage(stage: Stage) {
    val jobId = activeJobForStage(stage)
    if (jobId.isDefined) {
      logDebug("submitStage(" + stage + ")")
      if (!waitingStages(stage) && !runningStages(stage) && !failedStages(stage)) {
        val missing = getMissingParentStages(stage).sortBy(_.id)
        logDebug("missing: " + missing)
        if (missing.isEmpty) {
          logInfo("Submitting " + stage + " (" + stage.rdd + "), which has no missing parents")
          submitMissingTasks(stage, jobId.get)
        } else {
          for (parent <- missing) {
            submitStage(parent)
          }
          waitingStages += stage
        }
      }
    } else {
      abortStage(stage, "No active job for stage " + stage.id, None)
    }
  }

每次找到窄依赖会进行压栈操作,当所有窄依赖都找到完毕以后,会返回missing集合,missing中存储的是还没有向内继续切割的不完整的stage。getMissingParentStages方法如下:

private def getMissingParentStages(stage: Stage): List[Stage] = {
    val missing = 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(rdd: RDD[_]) {
      if (!visited(rdd)) {
        visited += rdd
        val rddHasUncachedPartitions = getCacheLocs(rdd).contains(Nil)
        if (rddHasUncachedPartitions) {
          for (dep <- rdd.dependencies) {
            dep match {
              case shufDep: ShuffleDependency[_, _, _] =>
                val mapStage = getShuffleMapStage(shufDep, stage.firstJobId)
                if (!mapStage.isAvailable) {
                  missing += mapStage
                }
              case narrowDep: NarrowDependency[_] =>
                waitingForVisit.push(narrowDep.rdd)
            }
          }
        }
      }
    }
    waitingForVisit.push(stage.rdd)
    while (waitingForVisit.nonEmpty) {
      visit(waitingForVisit.pop())
    }
    missing.toList
  }

当stage划分完毕以后,会将每个stage封装成Task,并最后将Task集合发送给executor去执行。

ps:优化

(1)consolidationFile参数打开,这样只在shuffle的第一个并行阶段创建buffer和对应的file,后面的executor执行Task不在继续创建文件,减少了文件创建的代价。

(2)上游ShuffleMapTask输出文件以后,下游ResultTask拉取数据时,可调整每次拉取数据的多少参数,这个要视情况而定,如果吗茫然设置过大则可能发生OOM的风险。

(3)可调整数据本地化级别参数,调整本地化级别参数的等待时间,如果为了本地化而等待时间过长,拖慢整个spark作业,则也不值得,所以要全横设置。

(4)多次重复使用的数据要进行persist存储,以免下次计算。

(5)如果多次使用某些固定数据时,比如字典数据,则需要使用Broadcast,这样每个executor进程中存在一份,而不会每个Task中复制一份。

(6)使用Kryo序列化框架,更快的序列化更好的压缩

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!