ParallelCollectionRDD 到 DataFrame 生成步骤
- 通过 spark.sparkContext.parallelize(Seq) 创建 ParallelCollectionRDD
- 通过 implicit 方法
rddToDatasetHolder()
,进入_sqlContext.createDataset(rdd)
- rdd被包装为 SerializeFromObject 对象(通过 ExternalRDD的apply()方法)
- 通过
import sparkSession.implicits._
可以自动帮我们引入非常多的spark内部自动转换 - ExternalRDD 类层次结构: ExternalRDD --> LeafNode --> LogicalPlan
encoderFor[T]
方法 : 在SQLImplicits implicit 引入了ExpressionEncoder()对象(object.apply() 方法中反射类属性得到ExpressionEncoder对象)
,并通过implicitly[Encoder[A]]
得到该实例对象- 通过
CatalystSerde.generateObjAttr[T]
反射获取RDD中对象的属性作为ExternalRDD的Attribute:AttributeReference('obj',T, nullable)
- 通过Encoder中的Schema填充Seq[NamedExpression],生成SerializeFromObject对象, SerializeFromObject 类结构:SerializeFromObject --> ObjectConsumer --> UnaryNode --> LogicalPlan
- SerializeFromObject 被包装为 Dataset
- 进入Dataset构造方法
- 对上述 LogicalPlan对象封装 QueryExecution对象,
- QueryExecution调用analyzed方法,分析内部的logicalPlan.
sparkSession.sessionState.analyzer.executeAndCheck(logical)
- 调用
encoderFor(encoder)
,exprEnc.resolveAndBind(logicalPlan.output, sparkSession.sessionState.analyzer)
反射绑定Schema 信息,中间调用的 analyzer: Analyzer为 BaseSessionStateBuild内部对象
返回 Dataset 实例
- 将 RDD 转换为 DatasetHolder 对象,再调用 toDF() 方法,toDF() 方法实际上还是在构建一个Dataset对象不过数据范型为[ROW],encoder=RowEncoder(schema)
createOrReplaceTempView 执行过程
-
创建
LogicalPlan
子类CreateViewCommand
,类层次关系:CreateViewCommand --> RunnableCommand --> Command --> LogicalPlan -
withPlan()方法内部使用使用上面的logicalPlan,调用
Dataset.ofRows
方法生成 Dataset 对象,实现过程和上面SerializeFromObject 被包装为 Dataset类似2.1 对logicalPlan封装为 QueryExecution
2.2 analyze logicalPlan
2.3 创建 Dataset 对象( 执行init方法 )
2.4 Dataset.logicalPlan 对象实例化
2.4.1 此时会先对传入的 queryExecution 进行 analyze 2.4.2 如果结果为Command对象,会执行该Command
2.4.2 执行详细说明
- 执行Command方法说明
withAction("command", queryExecution)(_.executeCollect())
name: String : 命令名
qe: QueryExecution :
action : 对SparkPlan进行转换的函数,实际上是调用对 qe.executedPlan 进行执行,并在执行对前后进行timeCostd等metrics进行统计
- Dataset.withAction() 方法内容
SQLExecution.withNewExecutionId --> call --> action(qe.executedPlan) - qe.executedPlan生成物理执行计划
lazy init executedPlan: SparkPlan = prepareForExecution(sparkPlan)
- 使用物理计划生成规则,生成 exetutedPlan, AE 模式少了CollapseCodegenStages,ReuseExchange,多了 PlanQueryStage
// 普通优化器
python.ExtractPythonUDFs,
PlanSubqueries(sparkSession),
EnsureRequirements(sparkSession.sessionState.conf),
CollapseCodegenStages(sparkSession.sessionState.conf),
ReuseExchange(sparkSession.sessionState.conf),
ReuseSubquery(sparkSession.sessionState.conf)
// AE 优化器
python.ExtractPythonUDFs,
PlanSubqueries(sparkSession),
EnsureRequirements(sparkSession.sessionState.conf),
ReuseSubquery(sparkSession.sessionState.conf),
// PlanQueryStage needs to be the last rule because it divides the plan into multiple sub-trees
// by inserting leaf node QueryStageInput. Transforming the plan after applying this rule will
// only transform node in a sub-tree.
PlanQueryStage(sparkSession.sessionState.conf)
-
执行command(
ExecutedCommandExec
) executeCollect() 方法4.1 原始的 SparkPlan.executeCollect() 方法是调用collect() 方法得到byteArrayRdd,然后把结果收集到本地
4.2 SubQueryExec实现: 启动一个线程,执行当前SparkPlan 并返回结果
4.3 AE 的实现:QueryStage.executeCollect()
- prepareExecuteStage()
- 执行子任务
- 优化器优化当前SparkPlan
- codeGen and update the UI
- child.executeCollect()
4.4 在 ExecutedCommandExec.executeCollect() 方法被指向了 lazy变量 sideEffectResult,通过这个对象调用对应的 RunnableCommand.run() 方法,只执行一次,并缓存执行结果
- prepareExecuteStage()