以ShuffleMapStage 爲例進行解析。
1 假設
RDD A – ShuffleDependency – RDD B
即 RDD B依賴 RDD A,並且依賴關係爲寬依賴—— 依賴,針對的是兩個RDD之間的關係,RDD可以有多個父依賴RDD,但針對每個父依賴RDD都會有對應的具體依賴。
2 關鍵源碼及其解析
關鍵源碼爲DAGScheduler類中構建ShuffleMapStage 的代碼—— 該方法中同時包含了Stage複用的概念。
/** * Create a shuffle map Stage for the given RDD. The stage will also be associated with the * provided firstJobId. If a stage for the shuffleId existed previously so that the shuffleId is * present in the MapOutputTracker, then the number and location of available outputs are * recovered from the MapOutputTracker */ private def newOrUsedShuffleStage( shuffleDep: ShuffleDependency[_, _, _], firstJobId: Int): ShuffleMapStage = {
// 根據假設: RDD B 依賴 RDD A,此時,shuffleDep表示兩者直接的依賴關係, // 對應的shuffleDep.rdd 爲 RDD A, 即RDD A 是當前Stage的最後一個RDD val rdd = shuffleDep.rdd // 傳入的任務數numTasks,對應的是Stage 最後一個RDD(即RDD A) // 的分區個數。 // 而在後續由 ShuffleMapStage構建 ShuffleMapTask 時numTasks會 // 控制Task的個數,因此該Stage 的並行度是由其最後一個RDD的分區控制。 // 對應也體現了,是被寬依賴的 RDD A 需要根據 RDD B 所需的數據結構進行重組 // 重組的數據存入磁盤中(當前實現) val numTasks = rdd.partitions.length val stage = newShuffleMapStage(rdd, numTasks, shuffleDep, firstJobId, rdd.creationSite) if (mapOutputTracker.containsShuffle(shuffleDep.shuffleId)) { val serLocs = mapOutputTracker.getSerializedMapOutputStatuses(shuffleDep.shuffleId) val locs = MapOutputTracker.deserializeMapStatuses(serLocs) (0 until locs.length).foreach { i => if (locs(i) ne null) { // locs(i) will be null if missing stage.addOutputLoc(i, locs(i)) } } } else { // Kind of ugly: need to register RDDs with the cache and map output tracker here // since we can't do it in the RDD constructor because # of partitions is unknown logInfo("Registering RDD " + rdd.id + " (" + rdd.getCreationSite + ")") mapOutputTracker.registerShuffle(shuffleDep.shuffleId, rdd.partitions.length) } stage } |
ShuffleMapTask構建的對應代碼在DAGScheduler類的submitMissingTasks方法中,具體如下:
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) } |
3 擴展
Stage 是由最後一個RDD的分區數控制並行度,因此在進行重分區時,需要注意最後的分區個數如果過小,則並行度降低,進而影響效率。此時可以採用寬依賴,切分Stage,保證前面的並行度不會降低。
比如,使用coalesce將大分區數合併成小分區數時,由於沒有Shuffle(默認),此時最終執行的並行度爲合併後的小分區個數 —— 可以考慮默認Shuffle爲true的 repartition方法,認爲加上寬依賴,保證前面計算的高並行度。