Spark修炼之道(高级篇)——Spark源代码阅读:第十二节 Spark SQL 处理流程分析
作者:周志湖
以下的代码演示了通过Case Class进行表Schema定义的样例:
// sc is an existing SparkContext.
val sqlContext = new org.apache.spark.sql.SQLContext(sc)
// this is used to implicitly convert an RDD to a DataFrame.
import sqlContext.implicits._
// Define the schema using a case class.
// Note: Case classes in Scala 2.10 can support only up to 22 fields. To work around this limit,
// you can use custom classes that implement the Product interface.
case class Person(name: String, age: Int)
// Create an RDD of Person objects and register it as a table.
val people = sc.textFile("examples/src/main/resources/people.txt").map(_.split(",")).map(p => Person(p(0), p(1).trim.toInt)).toDF()
people.registerTempTable("people")
// SQL statements can be run by using the sql methods provided by sqlContext.
val teenagers = sqlContext.sql("SELECT name, age FROM people WHERE age >= 13 AND age <= 19")
// The results of SQL queries are DataFrames and support all the normal RDD operations.
// The columns of a row in the result can be accessed by field index:
teenagers.map(t => "Name: " + t(0)).collect().foreach(println)
// or by field name:
teenagers.map(t => "Name: " + t.getAs[String]("name")).collect().foreach(println)
// row.getValuesMap[T] retrieves multiple columns at once into a Map[String, T]
teenagers.map(_.getValuesMap[Any](List("name", "age"))).collect().foreach(println)
// Map("name" -> "Justin", "age" -> 19)
(1)sql方法返回DataFrame
def sql(sqlText: String): DataFrame = {
DataFrame(this, parseSql(sqlText))
}
当中parseSql(sqlText)方法生成对应的LogicalPlan得到,该方法源代码例如以下:
//依据传入的sql语句,生成LogicalPlan
protected[sql] def parseSql(sql: String): LogicalPlan = ddlParser.parse(sql, false)
ddlParser对象定义例如以下:
protected[sql] val sqlParser = new SparkSQLParser(getSQLDialect().parse(_))
protected[sql] val ddlParser = new DDLParser(sqlParser.parse(_))
(2)然后调用DataFrame的apply方法
private[sql] object DataFrame {
def apply(sqlContext: SQLContext, logicalPlan: LogicalPlan): DataFrame = {
new DataFrame(sqlContext, logicalPlan)
}
}
能够看到,apply方法參数有两个,各自是SQLContext和LogicalPlan,调用的是DataFrame的构造方法,详细源代码例如以下:
//DataFrame构造方法。该构造方法会自己主动对LogicalPlan进行分析,然后返回QueryExecution对象
def this(sqlContext: SQLContext, logicalPlan: LogicalPlan) = {
this(sqlContext, {
val qe = sqlContext.executePlan(logicalPlan)
//推断是否已经创建。假设是则抛异常
if (sqlContext.conf.dataFrameEagerAnalysis) {
qe.assertAnalyzed() // This should force analysis and throw errors if there are any
}
qe
})
}
(3)val qe = sqlContext.executePlan(logicalPlan) 返回QueryExecution, sqlContext.executePlan方法源代码例如以下:
protected[sql] def executePlan(plan: LogicalPlan) =
new sparkexecution.QueryExecution(this, plan)
QueryExecution类中表达了Spark运行SQL的主要工作流程,详细例如以下
class QueryExecution(val sqlContext: SQLContext, val logical: LogicalPlan) {
@VisibleForTesting
def assertAnalyzed(): Unit = sqlContext.analyzer.checkAnalysis(analyzed)
lazy val analyzed: LogicalPlan = sqlContext.analyzer.execute(logical)
lazy val withCachedData: LogicalPlan = {
assertAnalyzed()
sqlContext.cacheManager.useCachedData(analyzed)
}
lazy val optimizedPlan: LogicalPlan = sqlContext.optimizer.execute(withCachedData)
// TODO: Don't just pick the first one...
lazy val sparkPlan: SparkPlan = {
SparkPlan.currentContext.set(sqlContext)
sqlContext.planner.plan(optimizedPlan).next()
}
// executedPlan should not be used to initialize any SparkPlan. It should be
// only used for execution.
lazy val executedPlan: SparkPlan = sqlContext.prepareForExecution.execute(sparkPlan)
/** Internal version of the RDD. Avoids copies and has no schema */
//调用toRDD方法运行任务将结果转换为RDD
lazy val toRdd: RDD[InternalRow] = executedPlan.execute()
protected def stringOrError[A](f: => A): String =
try f.toString catch { case e: Throwable => e.toString }
def simpleString: String = {
s"""== Physical Plan ==
|${stringOrError(executedPlan)}
""".stripMargin.trim
}
override def toString: String = {
def output =
analyzed.output.map(o => s"${o.name}: ${o.dataType.simpleString}").mkString(", ")
s"""== Parsed Logical Plan ==
|${stringOrError(logical)}
|== Analyzed Logical Plan ==
|${stringOrError(output)}
|${stringOrError(analyzed)}
|== Optimized Logical Plan ==
|${stringOrError(optimizedPlan)}
|== Physical Plan ==
|${stringOrError(executedPlan)}
|Code Generation: ${stringOrError(executedPlan.codegenEnabled)}
""".stripMargin.trim
}
}
能够看到,SQL的运行流程为
1.Parsed Logical Plan:LogicalPlan
2.Analyzed Logical Plan:
lazy val analyzed: LogicalPlan = sqlContext.analyzer.execute(logical)
3.Optimized Logical Plan:lazy val optimizedPlan: LogicalPlan = sqlContext.optimizer.execute(withCachedData)
4. Physical Plan:lazy val executedPlan: SparkPlan = sqlContext.prepareForExecution.execute(sparkPlan)
能够调用results.queryExecution方法查看,代码例如以下:
scala> results.queryExecution
res1: org.apache.spark.sql.SQLContext#QueryExecution =
== Parsed Logical Plan ==
'Project [unresolvedalias('name)]
'UnresolvedRelation [people], None
== Analyzed Logical Plan ==
name: string
Project [name#0]
Subquery people
LogicalRDD [name#0,age#1], MapPartitionsRDD[4] at createDataFrame at <console>:47
== Optimized Logical Plan ==
Project [name#0]
LogicalRDD [name#0,age#1], MapPartitionsRDD[4] at createDataFrame at <console>:47
== Physical Plan ==
TungstenProject [name#0]
Scan PhysicalRDD[name#0,age#1]
Code Generation: true
(4) 然后调用DataFrame的主构造器完毕DataFrame的构造
class DataFrame private[sql](
@transient val sqlContext: SQLContext,
@DeveloperApi @transient val queryExecution: QueryExecution) extends Serializable
(5)
当调用DataFrame的collect等方法时,便会触发运行executedPlan
def collect(): Array[Row] = withNewExecutionId {
queryExecution.executedPlan.executeCollect()
}
比如:
scala> results.collect
res6: Array[org.apache.spark.sql.Row] = Array([Michael], [Andy], [Justin])
总体流程图例如以下:
Spark修炼之道(高级篇)——Spark源代码阅读:第十二节 Spark SQL 处理流程分析的更多相关文章
- Spark修炼之道——Spark学习路线、课程大纲
课程内容 Spark修炼之道(基础篇)--Linux基础(15讲).Akka分布式编程(8讲) Spark修炼之道(进阶篇)--Spark入门到精通(30讲) Spark修炼之道(实战篇)--Spar ...
- 【转】【技术博客】Spark性能优化指南——高级篇
http://mp.weixin.qq.com/s?__biz=MjM5NjQ5MTI5OA==&mid=2651745207&idx=1&sn=3d70d59cede236e ...
- Spark性能优化指南——高级篇
本文转载自:https://tech.meituan.com/spark-tuning-pro.html 美团技术点评团队) Spark性能优化指南——高级篇 李雪蕤 ·2016-05-12 14:4 ...
- Spark性能优化指南-高级篇(spark shuffle)
Spark性能优化指南-高级篇(spark shuffle) 非常好的讲解
- Spark修炼之道(进阶篇)——Spark入门到精通:第九节 Spark SQL执行流程解析
1.总体执行流程 使用下列代码对SparkSQL流程进行分析.让大家明确LogicalPlan的几种状态,理解SparkSQL总体执行流程 // sc is an existing SparkCont ...
- 【转载】Spark性能优化指南——高级篇
前言 数据倾斜调优 调优概述 数据倾斜发生时的现象 数据倾斜发生的原理 如何定位导致数据倾斜的代码 查看导致数据倾斜的key的数据分布情况 数据倾斜的解决方案 解决方案一:使用Hive ETL预处理数 ...
- Spark性能优化指南——高级篇(转载)
前言 继基础篇讲解了每个Spark开发人员都必须熟知的开发调优与资源调优之后,本文作为<Spark性能优化指南>的高级篇,将深入分析数据倾斜调优与shuffle调优,以解决更加棘手的性能问 ...
- Spark性能优化指南-高级篇
转自https://tech.meituan.com/spark-tuning-pro.html,感谢原作者的贡献 前言 继基础篇讲解了每个Spark开发人员都必须熟知的开发调优与资源调优之后,本文作 ...
- Spark性能调优-高级篇
前言 继基础篇讲解了每个Spark开发人员都必须熟知的开发调优与资源调优之后,本文作为<Spark性能优化指南>的高级篇,将深入分析数据倾斜调优与shuffle调优,以解决更加棘手的性能问 ...
随机推荐
- 模板层 Template
每一个 Web 框架都需要一种很便利的方法用于动态生成 HTML 页面. 最常见的做法是使用模板. 模板包含所需 HTML 页面的静态部分,以及一些特殊的模版语法,用于将动态内容插入静态部分. 说白了 ...
- H5教程:移动页面性能优化
随着移动互联网的发展,我们越发要关注移动页面的性能优化,今天跟大家谈谈这方面的事情. 首先,为什么要最移动页面进行优化? 纵观目前移动网络的现状,移动页面布局越来越复杂,效果越来越炫,直接导致了文件越 ...
- js 监听ios手机键盘弹起和收起的事件
document.body.addEventListener('focusin', () => { //软键盘弹起事件 console.log("键盘弹起") }) docu ...
- tinymce原装插件源码分析(一)-hr
tinymce简介 tinymce是一款能方便无限扩展的网页富文本编辑器. tinymce原装插件已经十分丰富,对于文本编辑(blog等文章)是绰绰有余,但是应对一些复杂的应用,比如在上面开发html ...
- CodeForces-1007A Reorder the Array 贪心 田忌赛马
题目链接:https://cn.vjudge.net/problem/CodeForces-1007A 题意 给个数组,元素的位置可以任意调换 问调换后的元素比此位置上的原元素大的元素个数最大多少 思 ...
- [TJOI2015]弦论(后缀数组or后缀自动机)
解法一:后缀数组 听说后缀数组解第k小本质不同的子串是一个经典问题. 把后缀排好序后第i个串的本质不同的串的贡献就是\(n-sa[i]+1-LCP(i,i-1)\)然后我们累加这个贡献,看到哪一个串的 ...
- [Vijos P1369]难解的问题
题目大意:给你一个序列,叫你求最长上升子序列长度,但必须包含第k项. 解题思路:我们把k左边的比a[k]大的数去掉,k右边的比k小的数去掉,就可以保证选到a[k]了(因为左边的数小于a[k],而a[k ...
- 按shift键调出命令行的脚本
打开Notepad++,粘贴以下命令,并将文件命名为opencmdhere.reg(注意:文件编码格式为UCS-2 Little Endian,否则会导致中文乱码),再双击打开即可 Windows R ...
- Android之——拦截短信
转载请注明出处:http://blog.csdn.net/l1028386804/article/details/46994097 这里.向大家简介通过BroadcastReceiver来拦截短信的方 ...
- Android触碰事件
OnTouchListener使用 public class ViewActivity extends Activity implements View.OnTouchListener { @Over ...