1、《apache spark 源码剖析》浏览第六、七、八、九章
后面的几章中只准备学习其中的spark sql部分,所以首先全部浏览了一下,再回过头来看第七章
2、读《apache spark 源码剖析》第七章第1节、第2.1节
SQL语句在分析执行过程中会经理几个步骤:(1)语法解析。(2)操作绑定。(3)优化执行策略。(4)交付执行。
3、源码学习
从书中看出入口类为SparkContext,但是看代码中,在新版本已经被SparkSession代替,所以直接看SparkSession,书中sql方法返回的SchemaRdd,现在也已经变成了DataFrame
类SparkSession
sql方法首先调用sessionState.sqlParser.parsePlan(sqlText)方法生成LogicalPlan
那么首先要找到这个sqlParser的具体实例类
看sessionState创建的地方,先调用SparkSession.sessionStateClassName方法来确定BaseSessionStateBuilder的具体实现类,我这里假定使用的hive,那么具体类为HiveSessionStateBuilder;然后调用SparkSession.instantiateSessionState,用反射方法创建一个HiveSessionStateBuilder,并调用他的build方法创建一个SessionState实例
类HiveSessionStateBuilder 继承自BaseSessionStateBuilder
类BaseSessionStateBuilder
在BaseSessionStateBuilder中,创建一个sqlParser要调用extensions.buildParser(session, new SparkSqlParser(conf))方法,SparkSqlParser类是一个spark sql的解析器
类SparkSessionExtensions
SparkSessionExtensions的buildParser是处理所有的外部注入的sqlParser,如果没有的话,就是返回刚才创建的SparkSqlParser
小细节
type
在SparkSessionExtensions类的开头有type ParserBuilder = (SparkSession, ParserInterface) => ParserInterface,代码看了半天没明白,网上找了解释
说的非常明白,把后面的ParserBuilder都替换成(SparkSession, ParserInterface) => ParserInterface就一目了然了
比如创建ParserInterface的buildParser方法(从上下文可以知道这个builder就是ParserBuilder ):
parserBuilders.foldLeft(initial) { (parser, builder) =>
builder(session, parser)
}
替换以后就很明白了,其实就是一个构造函数
parserBuilders.foldLeft(initial) { (parser, (SparkSession, ParserInterface) => ParserInterface) =>
(SparkSession, ParserInterface) => ParserInterface(session, parser)
}
foldleft
在SparkSessionExtensions类的buildParser方法中用到了buildParser函数
网上摘录的fold解释,foldleft类似
List中的fold方法需要输入两个参数:初始值以及一个函数。输入的函数也需要输入两个参数:累加值和当前item的索引。那么上面的代码片段发生了什么事?
代码开始运行的时候,初始值0作为第一个参数传进到fold函数中,list中的第一个item作为第二个参数传进fold函数中。
1、fold函数开始对传进的两个参数进行计算,在本例中,仅仅是做加法计算,然后返回计算的值;
2、Fold函数然后将上一步返回的值作为输入函数的第一个参数,并且把list中的下一个item作为第二个参数传进继续计算,同样返回计算的值;
3、第2步将重复计算,直到list中的所有元素都被遍历之后,返回最后的计算值,整个过程结束;
类AbstractSqlParser
下面接着前面的说,调用了sqlParser(即SparkSqlParser)的parsePlan方法,这个类中没有,在他的父类AbstractSqlParser中找到,这个方法为给定的sql语句创建LogicalPlan
在这个方法中首先调用了parse(sqlText)方法
在这个方法中,首先创建ANTLRNoCaseStringStream对象,这个对象继承自ANTLRInputStream,不同之处是ANTLRNoCaseStringStream把所有字符串改成了大写
创建ANTLRNoCaseStringStream对象后,连续创建了SqlBaseLexer、CommonTokenStream、SqlBaseParser对象,其中SqlBaseLexer、SqlBaseParser对象在源码中找不到,于是上网查了一下,发现是antler自动生成的(链接
http://www.jianshu.com/p/0aa4b1caac2e)
生成SqlBaseParser后,调用传入的toResult,在这里调用了sqlBaseParser的singleStatement方法创建一个SqlBaseParser.SingleStatementContext对象,然后将其作为参数,调用astBuilder.visitSingleStatement,这个astBuilder的具体实现类是SparkSqlAstBuilder,但是其中没有visitSingleStatement方法,该方法在其父类AstBuilder中,其中调用了visit方法,最终调用了AbstractParseTreeVisitor类的visit方法,后面有点乱了,需要学习antler才能继续,先不钻了
最终在SparkSession的sqlParser.parsePlan方法里取得了LogicalPlan,然后调用Dataset.ofRows方法,这个方法属于书中下一节的内容
类ANTLRInputStream
这个类是antlr包中的,从maven下载源码没下成功,于是直接到github下载了zip包,作为源码导入