• spark资源调度和任务调度


    资源调度:

    (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序列化框架,更快的序列化更好的压缩

  • 相关阅读:
    极客时间课程《数据结构与算法之美》笔记09
    Java 学习笔记02
    极客时间课程《数据结构与算法之美》笔记05
    极客时间课程《数据结构与算法之美》笔记04
    极客时间课程《数据结构与算法之美》笔记03
    极客时间课程《数据结构与算法之美》笔记02
    极客时间课程《数据结构与算法之美》01
    spring data jpa 通过对象传参
    生成流水号
    Java 为实体类动态添加属性
  • 原文地址:https://www.cnblogs.com/zzq-include/p/12019176.html
Copyright © 2020-2023  润新知