Impala 3.4 SQL查询梳理(一)

2022-05-20 08:32:17 浏览数 (2)

当通过jdbc请求连接至Impalad节点之后,我们提交的SQL会通过BE的JNI调用FE的api进行解析,主要的调用栈如下所示:

代码语言:javascript复制
query/executeAndWait(impala-beeswax-server.cc)
-Execute(impala-server.cc)
--ExecuteInternal(impala-server.cc)
---InitExecRequest(client-request-state.cc)
----RunFrontendPlanner(query-driver.cc)
-----GetExecRequest(frontend.cc)
------JniFrontendcreateExecRequest()
-------Frontend.createExecRequest()
--------Frontend.getTExecRequest()
---------Frontend.doCreateExecRequest()

在doCreateExecRequest方法中,会通过调用Parse.parse()来对SQL进行解析,解析完成之后,SQL就会变成对应的结构,如下所示:

从图中我们可以看到,Impala所有的SQL最终都是继承于StatementBase,包括select、alter、create等。这里我们以简单的select查询为例,最终SQL转换之后会被解析成SelectStmt这个类,而这个类其中又包含SelectList、FromClause等部分。通过Parse.parse()的解析,我们将一条普通的SQL转成了一个Impala的类。目前,Impala在进行SQL解析的时候,采用的是一个开源的框架antlr,关于这个框架不是本文描述的重点,这里就不再展开。 对于图中涉及到的一些接口和类,我们摘取了一部分代码中的注释,供大家参考。

  • ParseNode: divide into two broad categories: statement-like nodes and expression nodes;
  • StmtNode: Base interface for statements and statement-like nodes such as clauses;
  • Expr: Root of the expr node hierarchy;
  • StatementBase: Base class for all Impala SQL statements;
  • QueryStmt: Abstract base class for any statement that returns results via a list of result expressions;

在解析出了具体的StatementBase之后(上述例子中就是SelectStmt),Impala接着会构造对应的Analyer,相关的类如下所示:

同样,我们截取部分代码中的注释来看一看:

  • AnalysisContext: Wrapper class for parsing, analyzing and rewriting a SQL stmt;
  • Analyzer: Repository of analysis state for single select block;
  • GlobalState: State shared between all objects of an Analyzer tree.

这里最重要的类就是Analyzer,包括了单个select查询块的所有解析之后的状态集合。我们继续以SelectStmt为例来看下生成Analyzer的接口调用流程:

代码语言:javascript复制
Frontend.doCreateExecRequest()
-AnalysisContext.analyzeAndAuthorize()
--AnalysisContext.analyze()
---SelectStmt.analyze()
----SelectStmt.SelectAnalyzer.analyze()

我们可以看到,主要就是调用各个StatementBase子类的analyze(),来实现对各个查询的解析。这里简单看一下SelectStmt的analyze方法,如下所示:

代码语言:javascript复制
  // SelectStmt.analyze()
  public void analyze(Analyzer analyzer) throws AnalysisException {
   if (isAnalyzed()) return;
   super.analyze(analyzer);
   new SelectAnalyzer(analyzer).analyze();
 }
 
 // SelectStmt.SelectAnalyzer.analyze()
 private void analyze() throws AnalysisException {
     // Start out with table refs to establish aliases.
     fromClause_.analyze(analyzer_);

     analyzeSelectClause();
     verifyResultExprs();
     registerViewColumnPrivileges();
     analyzeWhereClause();
     createSortInfo(analyzer_);
     ......

可以看到,SelectStmt的解析,主要都在其私有类SelectAnalyzer的analyze中进行处理了,这里包括了对于FromClause的处理、WhereClause的处理等操作。其他的SQL也是类似处理流程,每一个具体的SQL类都有对应的analyze方法。解析完成之后,Impala就会根据解析的结果来生成相应地执行计划:首先是生成一个单机的执行计划,接着会根据单机的执行计划来生成分布式的执行计划。关于执行计划的生成这块,我们会在后续的文章里面陆续提到,这里就不再展开描述。执行计划生成之后,Backend模块就会根据这些执行计划执行实际的扫描、聚合运算等操作,最终返回结果。 我们从第一幅图可以看到,ParseNode主要分为了两个部分:1)StmtNode,这个主要包括查询以及相应的clause实现;2)Expr,我们接下来就看一看这个Expr相应的各个子类都是什么样的,下面就是一个简单的关于UML的类图:

从上图可以看到,有非常多的类都继承了Expr,这里我们看几个比较常见的类:

  • Predicate,这个类就是用来保存各种谓词条件的,包括:BetweenPredicate、BinaryPredicate等,我们在上述的SelectStmt中提到的whereClasue_最终就会转换成一个Predicate,根据不同的条件转换成相应的Predicate;
  • LiteralExpr,用来保存各种常量的值,例如布尔保存在BoolLiteral中,字符串保存在StringLiteral中等等,目前主要就包括图中的这其中;
  • FunctionCallExpr,各种函数调用,最终都会转换成这个对象,例如常见的count、sum等;
  • SlotRef,这个可以简单理解为列的描述,SQL中涉及到列都会被转换成一个SlotRef对象,保存着这个列的相关信息;
  • 其他还有一些例如AnalyticExpr、CastExpr等这里就不再展开描述,感兴趣的同学可以自行查看相关的源码。

下面我们就从一个具体的SQL出来,来简单看一下上面提到的各个对象是如何解析的,SQL如下:

代码语言:javascript复制
select id,user,count(1) from table_name
where id>=5 and id<=10
group by id,user order by id desc;

结合上面的几个类图,我们可以看看上述的SQL会被解析成什么样的:

  • SelectList包含三个SelectListItem,分别是:id、user和count(1),而这三个item各自包含的Expr分别是:SlotRef、SlotRef和FunctionCallExpr,而这个FunctionCallExpr本身又包含一个NumericLiteral,对应count(1)里面的1;
  • fromClause_主要包括了一个表的集合,这里只有一个成员,就是table_name;
  • whereClasue_这里转换成了一个CompoundPredicate的谓词,表示组合的谓词,操作符是AND。它本身又包含两个BinaryPredicate,表示包含两个操作数的谓词,分别对应id>=5和id<=10。以第一个为例,它的操作符是>=,本身又包含两个child,分别是id对应的SlotRef以及10对应的NumericLiteral;
  • groupingExprs_是一系列的group by成员集合,这里主要就是包括两个SlotRef,分别对应id和user;
  • orderByElements_是从QueryStmt继承而来,成员是一个OrderByElement类,而这个OrderByElement内部也是包含了一个Expr,这里对应的仍旧是一个SlotRef,即id列; 到这里,我们基本对于上述示例中的SQL各个部分的解析都已经完成了。 本文比较浅显地讲述了Impala SQL解析中的两个部分:StmtmentBase和Expr,整个SQL解析的大部分成员对象,最终都会转换成这两个类或者其子类。关于Analyzer类,本身没有过多讲述,只是稍微提了一下,后续有机会再跟大家一起深入分享。

0 人点赞