注: 本文的思考和理解可能还不够完善, 毕竟知识水平有限, 还是作学习记录和思路参考吧, 随时可能修改和更新. 如有错误, 恳请更正
之前学习Spark的时候, 关于宽窄依赖以及stage划分, 一直都知道几句话:
- 宽依赖和窄依赖的一个重要区别是有无shuffle
- 根据宽依赖来进行stage划分
- 在DAG中进行反向解析, 遇到宽依赖就断开, 遇到窄依赖就把当前的RDD加入到当前的阶段中
- 以及一幅经典的图
虽然知道了, 但还是尽量要知其所以然的. 学习源码的设计思想也是学习中挺好的过程
接下来, 直接看涉及stage划分的这个方法吧
这个方法定位起来也很简单, 写一个Action操作, 比如foreach(println), 然后按照调用的一个个方法去找就行了
先说一说我在这段stage划分源码中看到的设计吧:
1. 用HashSet对stage和RDD去重, 防止重复计算, 并且HashSet提高了性能
2. 构建一个stack来进行递归寻找父stage, 代替递归方法的调用, 免除了寻找父stage的长过程中, JVM发生StackOverflowError的可能性
3. 用DFS遍历的方式来寻找父stage
先看第一行. 传入的参数是Stage, 调用这个方法的时候传入的stage叫做finalStage. 什么意思呢? 顾名思义, 最后一个stage. 也就是说, 传入最末端的那个stage, 从它开始寻找父级stage(人家方法名字就叫getMissingParentStages, 也很好理解). 返回的值是List[Stage], 看来是一堆stage
此时有必要看一下Stage的构造器, 比较重要的是它包括了一个RDD集合 & 一个parents集合(指的是父级和父级的父级们, 不是父母的意思) & 一个firstJobId, 后面都会用到
这里, 有必要结合这幅图理一下几个概念:
- task. 就是右图中两个绿色框内的部分, 表示partition到partition的过程
- taskSet. 就是右图中红色框内的部分, 由一系列的task组成, 表示从RDD到RDD的过程
- taskSet = job
- stage中可以有不止一个RDD, 它们之间的转换过程就是job
- first job. 在stage2中, 从C到D的过程就是first job, 而D到F就不是
接着往下看, 方法