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

至此整个流程结束。

四、总结:

通过分析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——

原创文章,转载请注明:

转载自:OopsOutOfMemory盛利的Blog,作者: OopsOutOfMemory

本文链接地址:http://blog.csdn.net/oopsoom/article/details/37658021

注:本文基于署名-非商业性使用-禁止演绎 2.5 中国大陆(CC BY-NC-ND 2.5 CN)协议,欢迎转载、转发和评论,但是请保留本文作者署名和文章链接。如若需要用于商业目的或者与授权方面的协商,请联系我。

转自:http://blog.csdn.net/oopsoom/article/details/37658021

第一篇:Spark SQL源码分析之核心流程的更多相关文章

  1. 【Spark SQL 源码分析系列文章】

    从决定写Spark SQL源码分析的文章,到现在一个月的时间里,陆陆续续差不多快完成了,这里也做一个整合和索引,方便大家阅读,这里给出阅读顺序 :) 第一篇 Spark SQL源码分析之核心流程 第二 ...

  2. 第十一篇:Spark SQL 源码分析之 External DataSource外部数据源

    上周Spark1.2刚发布,周末在家没事,把这个特性给了解一下,顺便分析下源码,看一看这个特性是如何设计及实现的. /** Spark SQL源码分析系列文章*/ (Ps: External Data ...

  3. 第七篇:Spark SQL 源码分析之Physical Plan 到 RDD的具体实现

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

  4. 第九篇:Spark SQL 源码分析之 In-Memory Columnar Storage源码分析之 cache table

    /** Spark SQL源码分析系列文章*/ Spark SQL 可以将数据缓存到内存中,我们可以见到的通过调用cache table tableName即可将一张表缓存到内存中,来极大的提高查询效 ...

  5. 第十篇:Spark SQL 源码分析之 In-Memory Columnar Storage源码分析之 query

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

  6. Spark SQL源码解析(四)Optimization和Physical Planning阶段解析

    Spark SQL原理解析前言: Spark SQL源码剖析(一)SQL解析框架Catalyst流程概述 Spark SQL源码解析(二)Antlr4解析Sql并生成树 Spark SQL源码解析(三 ...

  7. Spark SQL源码解析(二)Antlr4解析Sql并生成树

    Spark SQL原理解析前言: Spark SQL源码剖析(一)SQL解析框架Catalyst流程概述 这一次要开始真正介绍Spark解析SQL的流程,首先是从Sql Parse阶段开始,简单点说, ...

  8. Spark SQL源码解析(五)SparkPlan准备和执行阶段

    Spark SQL原理解析前言: Spark SQL源码剖析(一)SQL解析框架Catalyst流程概述 Spark SQL源码解析(二)Antlr4解析Sql并生成树 Spark SQL源码解析(三 ...

  9. Spark SQL源码解析(三)Analysis阶段分析

    Spark SQL原理解析前言: Spark SQL源码剖析(一)SQL解析框架Catalyst流程概述 Spark SQL源码解析(二)Antlr4解析Sql并生成树 Analysis阶段概述 首先 ...

随机推荐

  1. nodejs 循环中操作需要同步执行解决方案

    最近用nodejs做了个针对某网站的小爬虫.干坏事得低调对吧,不能同时开太多的网络访问,结果各种回调/循环虐的心力交瘁. 经过了n次的百度\哥哥后终于拼出了自己要的功能.不敢独享分享出来以供大家参考. ...

  2. Vue.js_数据绑定

    一.文本 data {{data}} <div id="div1">{{message}}</div> <script> var div1 = ...

  3. 移动APP自动化测试框架

    简介 移动APP的UI自动化测试长久以来一直是一个难点,难点在于UI的”变”, 变化导致自动化用例的大量维护.从分层测试的角度,自动化测试应该逐层进行.最大量实现自动化测试的应该是单元测试,最容易实现 ...

  4. HDU 1233 还是畅通工程(Kruskal)

    还是畅通工程 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Sub ...

  5. JavaServlet实现下载功能

        我们在项目中经常会用到下载功能,所以今天我们先说下下载功能实现的思路,然后通过一个案例代码来具体体现. 1.下载的思路: ①首先要获取我们要操作的文件对象的路径 ②然后使用获取的文件对象路径构 ...

  6. Fiddler 抓包工具使用详解

    博客园 首页 新随笔 联系 订阅 管理 随笔 - 20  文章 - 0  评论 - 64 Fiddler 抓包工具总结 阅读目录 1. Fiddler 抓包简介 1). 字段说明 2). Statis ...

  7. OracleUNDO

    UNDO作用 数据的回滚 一致性读 表的闪回(事务,查询的闪回....) 失败会话的恢复 数据的回滚 SQL> rollback; 回滚的过程就是从回滚段里拿到刚刚执行的这条语句产生的回滚,然后 ...

  8. bash短路径显示

    修改.bashrc文件vim 打开.bashrc文件,找到如下这行,有两个,都修改一下: PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ ' 将上面 ...

  9. tomcat和jboss的区别

    1. Tomcat是Apache鼎力支持的Java Web应用服务器(注:servlet容器),由于它优秀的稳定性以及丰富的文档资料,广泛的使用人群,从而在开源领域受到最广泛的青睐. 2. Jboss ...

  10. window7安装MongoDB详细步骤

    1.下载安装包 下载地址:https://www.mongodb.com/download-center/community 2.鼠标右击安装包,进行安装 3.选自定义安装 4.千万不要勾选 5.打开 ...