/** Spark SQL源代码分析系列文章*/

自从去年Spark Submit 2013 Michael Armbrust分享了他的Catalyst,到至今1年多了,Spark SQL的贡献者从几人到了几十人,并且发展速度异常迅猛,究其原因,个人觉得有下面2点:

1、整合:将SQL类型的查询语言整合到 Spark 的核心RDD概念里。这样能够应用于多种任务,流处理,批处理,包含机器学习里都能够引入Sql。

    2、效率:由于Shark受到hive的编程模型限制,无法再继续优化来适应Spark模型里。

前一段时间測试过Shark,而且对Spark SQL也进行了一些測试,可是还是忍不住对Spark SQL一探到底,就从源码的角度来看一下Spark
SQL的核心运行流程吧。

一、引子

先来看一段简单的Spark SQL程序:

  1. 1. val sqlContext = new org.apache.spark.sql.SQLContext(sc)
  2. 2. import sqlContext._
  3. 3.case class Person(name: String, age: Int)
  4. 4.val people = sc.textFile("examples/src/main/resources/people.txt").map(_.split(",")).map(p => Person(p(0), p(1).trim.toInt))
  5. 5.people.registerAsTable("people")
  6. 6.val teenagers = sql("SELECT name FROM people WHERE age >= 13 AND age <= 19")
  7. 7.teenagers.map(t => "Name: " + t(0)).collect().foreach(println)

程序前两句1和2生成SQLContext,导入sqlContext以下的all,也就是执行SparkSQL的上下文环境。

程序3,4两句是载入数据源注冊table

第6句是真正的入口,是sql函数,传入一句sql,先会返回一个SchemaRDD。这一步是lazy的,直到第七句的collect这个action运行时,sql才会运行。

二、SQLCOntext

SQLContext是运行SQL的上下文对象,首先来看一下它Hold的有哪些成员:

Catalog  

 一个存储<tableName,logicalPlan>的map结构,查找关系的文件夹,注冊表,注销表,查询表和逻辑计划关系的类。

SqlParser 

 Parse 传入的sql来对语法分词,构建语法树,返回一个logical plan

Analyzer 

  logical plan的语法分析器

Optimizer 

 logical Plan的优化器

LogicalPlan 

逻辑计划,由catalyst的TreeNode组成,能够看到有3种语法树

SparkPlanner 

包括不同策略的优化策略来优化物理运行计划

QueryExecution 

sql运行的环境上下文



就是这些对象组成了Spark SQL的执行时,看起来非常酷,有静态的metadata存储,有分析器、优化器、逻辑计划、物理计划、执行执行时。

那这些对象是怎么相互协作来运行sql语句的呢?

三、Spark SQL运行流程

话不多说,先上图,这个图我用一个在线作图工具process on话的,画的不好,图能达意即可:





核心组件都是绿色的方框,每一步流程的结果都是蓝色的框框,调用的方法是橙色的框框。



先概括一下,大致的运行流程是:

Parse SQL -> Analyze Logical Plan -> Optimize Logical Plan -> Generate Physical Plan -> Prepareed Spark Plan -> Execute SQL -> Generate RDD



更详细的运行流程:

     sql or hql -> sql parser(parse)生成 unresolved logical plan -> analyzer(analysis)生成analyzed logical plan  -> optimizer(optimize)optimized logical plan -> spark planner(use strategies to plan)生成physical plan -> 採用不同Strategies生成spark
plan -> spark plan(prepare) prepared spark plan -> call toRDD(execute()函数调用) 运行sql生成RDD

3.1、Parse SQL

回到開始的程序,我们调用sql函数,事实上是SQLContext里的sql函数它的实现是new一个SchemaRDD,在生成的时候就调用parseSql方法了。

  1. /**
  2. * Executes a SQL query using Spark, returning the result as a SchemaRDD.
  3. *
  4. * @group userf
  5. */
  6. def sql(sqlText: String): SchemaRDD = new SchemaRDD(this, parseSql(sqlText))

结果是会生成一个逻辑计划

  1. @transient
  2. protected[sql] val parser = new catalyst.SqlParser
  3.  
  4. protected[sql] def parseSql(sql: String): LogicalPlan = parser(sql)

3.2、Analyze to Execution

当我们调用SchemaRDD里面的collect方法时,则会初始化QueryExecution,開始启动运行。

  1. override def collect(): Array[Row] = queryExecution.executedPlan.executeCollect()

我们能够非常清晰的看到运行步骤:

  1. protected abstract class QueryExecution {
  2. def logical: LogicalPlan
  3.  
  4. lazy val analyzed = analyzer(logical) //首先分析器会分析逻辑计划
  5. lazy val optimizedPlan = optimizer(analyzed) //随后优化器去优化分析后的逻辑计划
  6. // TODO: Don't just pick the first one...
  7. lazy val sparkPlan = planner(optimizedPlan).next() //依据策略生成plan物理计划
  8. // executedPlan should not be used to initialize any SparkPlan. It should be
  9. // only used for execution.
  10. lazy val executedPlan: SparkPlan = prepareForExecution(sparkPlan) //最后生成已经准备好的Spark Plan
  11.  
  12. /** Internal version of the RDD. Avoids copies and has no schema */
  13. lazy val toRdd: RDD[Row] = executedPlan.execute() //最后调用toRDD方法运行任务将结果转换为RDD
  14.  
  15. protected def stringOrError[A](f: => A): String =
  16. try f.toString catch { case e: Throwable => e.toString }
  17.  
  18. def simpleString: String = stringOrError(executedPlan)
  19.  
  20. override def toString: String =
  21. s"""== Logical Plan ==
  22. |${stringOrError(analyzed)}
  23. |== Optimized Logical Plan ==
  24. |${stringOrError(optimizedPlan)}
  25. |== Physical Plan ==
  26. |${stringOrError(executedPlan)}
  27. """.stripMargin.trim
  28. }

至此整个流程结束。

四、总结:

通过分析SQLContext我们知道了Spark SQL都包括了哪些组件,SqlParser,Parser,Analyzer,Optimizer,LogicalPlan,SparkPlanner(包括Physical Plan),QueryExecution.

  通过调试代码,知道了Spark SQL的运行流程:

sql or hql -> sql parser(parse)生成 unresolved logical plan -> analyzer(analysis)生成analyzed logical plan  -> optimizer(optimize)optimized logical plan -> spark planner(use strategies to
plan)生成physical plan -> 採用不同Strategies生成spark plan -> spark plan(prepare) prepared spark plan -> call toRDD(execute()函数调用) 运行sql生成RDD


  

  随后还会对里面的每一个组件对象进行研究,看看catalyst到底做了哪些优化。

  

  ——EOF——

原创文章:转载请注明出自:http://blog.csdn.net/oopsoom/article/details/37658021

Spark SQL源代码分析之核心流程的更多相关文章

  1. Spark SQL 源代码分析系列

    从决定写Spark SQL文章的源代码分析,到现在一个月的时间,一个又一个几乎相同的结束很快,在这里也做了一个综合指数,方便阅读,下面是读取顺序 :) 第一章 Spark SQL源代码分析之核心流程 ...

  2. Spark SQL 源代码分析之 In-Memory Columnar Storage 之 in-memory query

    /** Spark SQL源代码分析系列文章*/ 前面讲到了Spark SQL In-Memory Columnar Storage的存储结构是基于列存储的. 那么基于以上存储结构,我们查询cache ...

  3. Spark SQL 源代码分析之Physical Plan 到 RDD的详细实现

    /** Spark SQL源代码分析系列文章*/ 接上一篇文章Spark SQL Catalyst源代码分析之Physical Plan.本文将介绍Physical Plan的toRDD的详细实现细节 ...

  4. openVswitch(OVS)源代码分析之工作流程(数据包处理)

    上篇分析到数据包的收发,这篇开始着手分析数据包的处理问题.在openVswitch中数据包的处理是其核心技术,该技术分为三部分来实现:第一.根据skb数据包提取相关信息封装成key值:第二.根据提取到 ...

  5. openVswitch(OVS)源代码分析之工作流程(flow流表查询)

    原文链接: openVswitch(OVS)源代码分析之工作流程(flow流表查询)

  6. 第一篇:Spark SQL源码分析之核心流程

    /** Spark SQL源码分析系列文章*/ 自从去年Spark Submit 2013 Michael Armbrust分享了他的Catalyst,到至今1年多了,Spark SQL的贡献者从几人 ...

  7. Spark SQL概念学习系列之Spark SQL 架构分析(四)

    Spark SQL 与传统 DBMS 的查询优化器 + 执行器的架构较为类似,只不过其执行器是在分布式环境中实现,并采用的 Spark 作为执行引擎. Spark SQL 的查询优化是Catalyst ...

  8. Zepto源代码分析一~核心方法

    今天抽出时间复习了一下Zepto的源代码,依照自己的理解进行凝视. 欢迎大家拍砖. 源代码版本号:v1.1.4 源代码下载地址:http://zeptojs.com/ 分析总体代码之后,整理出架构图: ...

  9. Monkey源代码分析之执行流程

    在<MonkeyRunner源代码分析之与Android设备通讯方式>中.我们谈及到MonkeyRunner控制目标android设备有多种方法.当中之中的一个就是在目标机器启动一个mon ...

随机推荐

  1. C++--allocator类的使用

    C++为我们提供了安全的内存空间申请方式与释放方式,可是new与delete表达式却是把空间的分配回收与对象的构建销毁紧紧的关联在一起.实际上,作为与C语言兼容的语言,C++也为我们提供了更加底层的内 ...

  2. 获取Google音乐的具体信息(方便对Google音乐批量下载)

    Google音乐都是正版音乐, 不像百度所有都是盗链, 并且死链也多. 但有一个麻烦就是要下载Google音乐的时候得一个一个的点击下载链接, 进入下载页面再点"下载", 才干下载 ...

  3. HGE基础教程

    作者:寰子 来源:http://www.hgechina.com/前言: 写道: 无意中发现了hge中文社区,听朋友介绍,认识了hge,然后开始对它进行研究,并使用hge开始制作游戏. 因为我所得的资 ...

  4. for循环中一个不容小觑的问题

    for(int i=1;i<=100;i++) 作为程序猿,我们很喜欢使用这种for循环. 可是,当中隐含着一个重要的问题. 过多的编程经历可能使我们的思维产生了一些误解,在上面的for循环中, ...

  5. Android 性能优化 五 性能分析工具dumpsys的使用

    Android提供的dumpsys工具能够用于查看感兴趣的系统服务信息与状态,手机连接电脑后能够直接命令行运行adb shell dumpsys 查看全部支持的Service可是这样输出的太多,能够通 ...

  6. JAVA 保留两位小数的四种方法

    import java.math.BigDecimal; import java.text.DecimalFormat; import java.text.NumberFormat; publiccl ...

  7. text bss data的区别

    BSS段 在采用段式内存管理的架构中,BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域.BSS是英文Block Started by Symbol的简称.BSS ...

  8. Java重命名文件

    File file = new File("D:\\aa\a.txt");     file.renameTo(new File("D:\\aa\\b.txt" ...

  9. C#语音录制

    客服小妹是如何泡到手的——C#定时提醒·语音录制·语音播放·文件转录Demo——倾情奉献!   一.需求提出 客服小妹跟我说,每天要统计新加好友数,得先记下昨天的数目,然后查看今天的数目,还要相减,打 ...

  10. Access之C#连接Access

    原文:Access之C#连接Access 如果是个人用的小程序的话.一般都推荐用Sqlite和Access 使用SQlite数据库需要安装SQLite驱动,详情:SQLite之C#连接SQLite 同 ...