sparksql执行流程分析

本文深入探讨Spark SQL的执行流程,从Catalyst的整体执行流程出发,详细解析了SQL语句从Parser、Analyzer、Optimizer到SparkPlann的转化过程。通过实例展示了一个简单SQL语句的执行,解释了各个阶段的功能,如Parser使用ANTLR 4解析SQL,Analyzer利用Catalog解析Logical Plan,Optimizer应用多种策略进行优化,最后SparkPlann生成Physical Plan。此外,文章对比了Spark 2.x与1.x在性能上的改进,包括使用ANTLR4解析器、更丰富的优化策略以及引入第二代Tungsten引擎。
摘要由CSDN通过智能技术生成

    Spark sql是spark内部最核心,也是社区最活跃的组件。Spark SQL支持在Spark中执行SQL,或者HiveQL的关系查询表达式。列式存储的类RDD(DataSet/DataFrame)数据类型以及对sql语句的支持使它更容易上手,同时,它对数据的抽取、清洗的特性,使它广泛的用于etl,甚至是机器学习领域。因此,saprk sql较其他spark组件,获得了更多的使用者。

  下文,我们首先通过查看一个简单的sql的执行计划,对sql的执行流程有一个简单的认识,后面将通过对sql优化器catalyst的每个部分的介绍,来让大家更深入的了解sql后台的执行流程。由于此模板中代码较多,我们在此仅就执行流程中涉及到的主要代码进行介绍,方便大家更快地浏览spark sql的源码。本文中涉及到的源码spark 2.1.1的。

1. catalyst整体执行流程介绍

1.1 catalyst整体执行流程

说到spark sql不得不提的当然是Catalyst了。Catalyst是spark sql的核心,是一套针对spark sql 语句执行过程中的查询优化框架。因此要理解spark sql的执行流程,理解Catalyst的工作流程是理解spark sql的关键。而说到Catalyst,就必须得上下面这张图1了,这张图描述了spark sql执行的全流程。其中,长方形框内为catalyst的工作流程。

图1 spark sql 执行流程图

SQL语句首先通过Parser模块被解析为语法树,此棵树称为Unresolved Logical Plan;Unresolved Logical Plan通过Analyzer模块借助于Catalog中的表信息解析为Logical Plan;此时,Optimizer再通过各种基于规则的优化策略进行深入优化,得到Optimized Logical Plan;优化后的逻辑执行计划依然是逻辑的,并不能被Spark系统理解,此时需要将此逻辑执行计划转换为Physical Plan。

1.2 一个简单sql语句的执行

为了更好的对整个过程进行理解,下面通过一个简单的sql示例来查看采用catalyst框架执行sql的过程。示例代码如下:

object TestSql {



  case class Student(id:Long,name:String,chinese:String,math:String,English:String,age:Int)

  case class Score(sid:Long,weight1:String,weight2:String,weight3:String)

  

  def main(args: Array[String]): Unit = {

  //使用saprkSession初始化spark运行环境

  val spark=SparkSession.builder().appName("Spark SQL basic example").config("spark.some.config.option", "some-value").getOrCreate()

  //引入spark sql的隐式转换

   import sqlContext.implicits._

  //读取第一个文件的数据并转换成DataFrame

  val testP1 = spark.sparkContext.textFile("/home/dev/testP1").map(_.split(" ")).map(p=>Student(p(0).toLong,p(1),p(2),p(3),p(4),p(5).trim.toInt)).toDF()

  //注册成表

    testP1.registerTempTable("studentTable")

  //读取第二个文件的数据并转换成DataFrame

    val testp2 = spark.sparkContext.textFile("/home/dev/testP2").map(_.split(" ")).map(p=>Score(p(0).toLong,p(1),p(2),p(3))).toDF()

  //注册成表

    testp2.registerTempTable("scoreTable")

  //查看sql的逻辑执行计划

    val dataframe = spark.sql("select sum(chineseScore) from " +

      "(select x.id,x.chinese+20+30 as chineseScore,x.math from studentTable x inner join  scoreTable y on x.id=y.sid)z" +

      " where z.chineseScore <100").map(p=>(p.getLong(0))).collect.foreach(println)

此例也是针对spark2.1.1版本的,程序的入口是SparkSession。由于此例超级简单,做过spark的人一眼就能看出,而且每行代码都带有中文注释,所以在这里,我们就不做具体的介绍了。

  这里涉及到的sql查询就是最后一句,通过spark shell可以看到该sql查询的逻辑执行计划和物理执行计划。进入sparkshell后,输入一下代码即可显示此sql查询的执行计划。

  

spark.sql("select sum(chineseScore) from " +

      "(select x.id,x.chinese+20+30 as chineseScore,x.math from  studentTable x inner join  scoreTable y on x.id=y.sid)z" +

      " where z.chineseScore <100").explain(true)

这里,是使用DataSet的explain函数实现逻辑执行计划和物理执行计划的打印,调用explain的源码如下:

/**

* Prints the plans (logical and physical) to the console for debugging purposes.

*

* @group basic

* @since 1.6.0

*/

def explain(extended: Boolean): Unit = {

  val explain = ExplainCommand(queryExecution.logical, extended = extended)

  sparkSession.sessionState.executePlan(explain).executedPlan.executeCollect().foreach {

    // scalastyle:off println

    r => println(r.getString(0))

    // scalastyle:on println

  }

}

显示在spark shell中的unresolved logical plan、resolved logical plan、optimized logical plan和physical plan如下图2所示:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

野狼e族

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值