查看spark sql 源码
从上图可见,无论是直接使用 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。