spark sql 源码杂谈

查看spark sql 源码

 

Spark SQL Catalyst

从上图可见,无论是直接使用 SQL 语句还是使用 DataFrame,都会经过如下步骤转换成 DAG 对 RDD 的操作

  • Parser 解析 SQL,生成 Unresolved Logical Plan

  • 由 Analyzer 结合 Catalog 信息生成 Resolved Logical Plan

  • Optimizer根据预先定义好的规则对 Resolved Logical Plan 进行优化并生成 Optimized Logical Plan

  • Query Planner 将 Optimized Logical Plan 转换成多个 Physical Plan

  • CBO 根据 Cost Model 算出每个 Physical Plan 的代价并选取代价最小的 Physical Plan 作为最终的 Physical Plan

  • Spark 以 DAG 的方法执行上述 Physical Plan

  • 在执行 DAG 的过程中,Adaptive Execution 根据运行时信息动态调整执行计划从而提高执行效率

park SQL 使用 Antlr 进行记法和语法解析,并生成 UnresolvedPlan。

当用户使用 SparkSession.sql(sqlText : String) 提交 SQL 时,SparkSession 最终会调用 SparkSqlParser 的 parsePlan 方法。该方法分两步

  • 使用 Antlr 生成的 SqlBaseLexer 对 SQL 进行词法分析,生成 CommonTokenStream

  • 使用 Antlr 生成的 SqlBaseParser 进行语法分析,得到 LogicalPlan

看到了parse方法

  protected def parse[T](command: String)(toResult: SqlBaseParser => T): T = {
    logDebug(s"Parsing command: $command")

    val lexer = new SqlBaseLexer(new UpperCaseCharStream(CharStreams.fromString(command)))
    lexer.removeErrorListeners()
    lexer.addErrorListener(ParseErrorListener)

    val tokenStream = new CommonTokenStream(lexer)
    val parser = new SqlBaseParser(tokenStream)
    parser.addParseListener(PostProcessor)
    parser.removeErrorListeners()
    parser.addErrorListener(ParseErrorListener)

    try {
      try {
        // first, try parsing with potentially faster SLL mode
        parser.getInterpreter.setPredictionMode(PredictionMode.SLL)
        toResult(parser)
      }
      catch {
        case e: ParseCancellationException =>
          // if we fail, parse with LL mode
          tokenStream.seek(0) // rewind input stream
          parser.reset()

          // Try Again.
          parser.getInterpreter.setPredictionMode(PredictionMode.LL)
          toResult(parser)
      }
    }
    catch {
      case e: ParseException if e.command.isDefined =>
        throw e
      case e: ParseException =>
        throw e.withCommand(command)
      case e: AnalysisException =>
        val position = Origin(e.line, e.startPosition)
        throw new ParseException(Option(command), e.message, position, position)
    }
  }
  /**
   * Create a query execution object.
   */
  protected def createQueryExecution: LogicalPlan => QueryExecution = { plan =>
    new QueryExecution(session, plan)
  }

plan的值为这个  

'Project [*]
+- 'Filter ((((('vin = L6T78Y4WXGN060953) && ('c_time >= 1491358842)) && ('c_time <= 1491358887)) || ((('vin = L6T78Y4WXGN060953) && ('c_time >= 1491360817)) && ('c_time <= 1491360857))) || ((('vin = vin1) && ('c_time >= 1491360807)) && ('c_time <= 1491360847)))
   +- 'UnresolvedRelation `Df`
 

  def ofRows(sparkSession: SparkSession, logicalPlan: LogicalPlan): DataFrame = {
    val qe = sparkSession.sessionState.executePlan(logicalPlan)
    qe.assertAnalyzed()
    new Dataset[Row](sparkSession, qe, RowEncoder(qe.analyzed.schema))
  }
}
这个的目的是把logicplan变成QueryExecution
val qe = sparkSession.sessionState.executePlan(logicalPlan)

 

从parse函数可以看出,这里对于SQL语句的解析采用的是ANTLR 4 ,这里面使用了两个类:词法解析器SqlBaseLexer和语法解析器SqlBaseParser  

    SqlBaseLexer(词法解析器)和SqlBaseParser(语法解析器)均是使用ANTLR 4 自动生成的Java类。

      代码2中的sqlParser 为SparkSqlParser,其成员变量 val astBuilder = new SparkSqlAstBuilder(conf)是将antlr语法结构转换为catalyst表达式的关键类!

   可以看到code2中parsePlan方法先执行parse方法(code3),在代码4中先后实例化了分词解析器(SqlBaseLexer)和语法解析器(SqlBaseParser),最后将antlr的语法解析器parser:SqlBaseParser传给Code2中的柯里化函数,使用astBuilder转化为catalyst表达式,可以看到首先调用的是visitSingleStatement,singleStatement 为语法文件中定义的最顶级节点,接下来就是利用sntrl的visitor模式显示的便利整个语法树,将所有的节点都替换成了LogicalPlan或者Tableldentifier。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值