[Spark SQL] 源码解析之Analyzer

前言

由前面博客我们知道了SparkSql整个解析流程如下:

  • sqlText 经过 SqlParser 解析成 Unresolved LogicalPlan;
  • analyzer 模块结合catalog进行绑定,生成 resolved LogicalPlan;
  • optimizer 模块对 resolved LogicalPlan 进行优化,生成 optimized LogicalPlan;
  • SparkPlan 将 LogicalPlan 转换成PhysicalPlan;
  • prepareForExecution()将 PhysicalPlan 转换成可执行物理计划;
  • 使用 execute()执行可执行物理计划;

详解analyzer模块

Analyzer模块将Unresolved LogicalPlan结合元数据catalog进行绑定,最终转化为Resolved LogicalPlan。跟着代码看流程:

// 代码1
spark.sql("select * from table").show(false)
---
// 代码2
def sql(sqlText: String): DataFrame = {
    Dataset.ofRows(self, sessionState.sqlParser.parsePlan(sqlText))
  }
---
// 代码3
def ofRows(sparkSession: SparkSession, logicalPlan: LogicalPlan): DataFrame = {
    val qe = sparkSession.sessionState.executePlan(logicalPlan)
    qe.assertAnalyzed()
    new Dataset[Row](sparkSession, qe, RowEncoder(qe.analyzed.schema))
  }

代码2中的后半段sessionState.sqlParser.parsePlan(sqlText)在上篇博客已经解析,即将sqlText通过第三方解析器antlr解析成语法树。

接着进入代码3,通过Unresolved LogicalPlan创建QueryExecution对象, 这是一个非常关键的类,analyzer 、optimizer 、SparkPlan、executedPlan等都是在该类中触发的。继续跟着代码3走:

// 代码4
def assertAnalyzed(): Unit = {
    // Analyzer is invoked outside the try block to avoid calling it again from within the
    // catch block below.
    analyzed
   ...
// 代码5
lazy val analyzed: LogicalPlan = {
    SparkSession.setActiveSession(sparkSession)
    sparkSession.sessionState.analyzer.execute(logical)
  }

最终调用analyzer的execute方法,该方法在Analyzer的父类RuleExecutor中,另外还继承了CheckAnalysis 类,用于对 plan 做一些解析,如果解析失败则抛出用户层面的错误:

class Analyzer(
    catalog: SessionCatalog,
    conf: SQLConf,
    maxIterations: Int)
  extends RuleExecutor[LogicalPlan] with CheckAnalysis {
   

可以看到构造器中有SessionCatalog类型的catalog,此类管理着临时表、view、函数及外部依赖元数据(如hive metastore),是analyzer进行绑定的桥梁。

继承了RuleExecutor的类(Analyzer、Optimizer)需要实现def batches: Seq[Batch]方法,在execute方法中再对此batches进行遍历执行,batches 由多个Batch构成,每个Batch由多个Rule构成,看看Batch的定义protected case class Batch(name: String, strategy: Strategy, rules: Rule[TreeType]*),Strategy是每个Batch的执行策略即该batch被最大执行次数maxIterations ,Once和FixedPoint即执行一次和多次(默认是100次),停止执行batch的条件有两个,一是在执行maxIterations 次之前规则前后plan没有变化,二是执行次数达到maxIterations 。batch里面的所有规则都继承了Rule,在execute方法里就是遍历这些batchs,将所有的规则应用到LogicalPlan上。

接下来我们看看execute中具体是怎么做的:

def execute(plan: TreeType): TreeType = {
    var curPlan = plan
    //遍历batches
    batches.foreach { batch =>
      val batchStartPlan = curPlan
      var iteration = 1 //每个batch单独计数
      var lastPlan = curPlan //保存遍历batch之前的plan,以便和遍历后的plan进行比较,若无变化则停止执行当前batch
      var continue = true

      // Run until fix point (or the max number of iterations as specified in the strategy.
      while (continue) {
        curPlan = batch.rules.foldLeft(curPlan) { // 遍历一个batch所有的Rule,并应用到LogicalPlan上
          case (plan, rule) =>
            val
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值