Hive SQL编译原理(下)

living 发表于 2021/07/30 17:32:44 2021/07/30
【摘要】 三、过渡1 Driver.compile()2 SemanticAnalyzerFactory是Hive做语义解析的工厂类,用于根据SQL的类型,构建对应的语义解析器。比如:explain走ExplainSemanticAnalyzer,DDL走DDLSemanticAnalyzer,load走LoadSemanticAnalyzer等等,工厂模式可以使这些不同的功能隔离开,在一定程度上解耦...

三、过渡

1 Driver.compile()

3.png

2 SemanticAnalyzerFactory

是Hive做语义解析的工厂类,用于根据SQL的类型,构建对应的语义解析器。

比如:explain走ExplainSemanticAnalyzer,DDL走DDLSemanticAnalyzer,load走LoadSemanticAnalyzer等等,工厂模式可以使这些不同的功能隔离开,在一定程度上解耦,也增加了可扩展性,比如某天需要再添加个import数据的编译过程,开发个ImportSemanticAnalyzer类 在SemanticAnalyzerFactory工厂里注册一下就ok了。

一般执行查询时,都是进入default,构建SemanticAnalyzer或者CalcitePlanner(开启CBO)解析器

4.png

5.png

3 BaseSemanticAnalyzer

  • 抽象基类,不可以被实例化。
  • 它定义了语义分析器的入口,即analyze(ASTNode, Context),该入口做了:
    • Context保存在成员变量ctx中;
    • 调用 analyzeInternal(ASTNode)完成分析工作,这个函数是抽象函数,应该被各个子类分别override,以完成相应的分析逻辑。

6.png

4 Hive的语义分析器

语义分析主要通过 BaseSemanticAnalyzer 实现类中的 analyze 方法进行。不同的sql语句会用不同的 BaseSemanticAnalyzer实现类来进行分析,主要有以下语义分析器:

7.png

8.png

5 SemanticAnalyzer中的analyze方法

未开启CBO的情况下,查询语句的语义解析都是调用SemanticAnalyzer.analyze的,anlyze最终转向的是各个语义分析器中的 analyzeInternal 方法,SemanticAnalyzer中的analyzeInternal 代码如下:

1.png

2.png

从代码看,整个过程主要是以下的步骤:

  • 从抽象语法树生成 resolved parse tree AST->QB
  • 从 resolved parse tree 生成 op tree
  • 推断结果集表结构
  • 为优化器和物理编译器生成上下文
  • 执行逻辑优化
  • 优化物理执行树 & 翻译成目标执行引擎

四、阶段2-语义解析

1 语义解析总过程

经历了词法解析和语法解析后,SQL被编译成了ASTTreeAST Tree仍然非常复杂,不够结构化,不方便直接翻译为MapReduce程序,同时ASTTree缺少很多信息,比如涉及的表是外部表还是内部表,字段类型是什么,存储位置在哪,AST Tree转化为QueryBlock就是将SQL进一部抽象和结构化。

语义解析就是从ASTTree生成QueryBlock的过程,即从抽象语法树中找出所有的基本单元以及每个单元之间的关系的过程。每个基本单元创建一个QB对象,将每个基本单元的不同操作转化为QB对象的不同属性。

3.png

4.png

2 源码分析

2.1 SemanticAnalyzer中的genResolvedParseTree方法

5.png

6.png

7.png

2.2 doPhase1

doPhase1主要是递归地遍历AST,做基本的语义检查,并建立下面的映射关系表:

① 找到所有表、子查询的别名,并设置在aliasToTablealiasToSubq表中;

② 找到所有内部clause的结果的目标及其名字;

③ 建立从所有聚合操作的AST的字符串表示到其AST本身的映射关系;

④ 建立clause名字到其select表达式AST的映射关系;

⑤ 建立aliaslateral view的映射关系;

上面所有这些映射关系都保存在QB/QBParseInfo中。

参数qb是一个空的QB,在不同case类型下对齐进行填满。doPhase1ASTTree中的每个元素的TOK类型进行case,针对于不同的case对节点数据进行填充。
for
遍历整棵ASTTree,中间对每个元素递归调用doPhase1,这种方式是一种深度优先搜索的算法。经过一轮深度优先遍历,不带元数据的QB树就生成了。

8.png

2.3 getMetaData

doPhase1执行完毕之后得到QBQB里边的只是一些关键字还有一些表的名字,但是和hdfs的文件路径对应不起来,所以需要metaData映射关系,所以SemanticAnalyzer中调用了getMetaData()

该方法获取源表、目标表的元数据(主要是schema等信息),获取的元数据同样存储在QB/QBParseInfo中。

  • 获取source table的元数据,如果一个table实际上是一个view,将其重写为view的定义;
  • 递归的为每个子查询中的源表获得元数据;
  • 获取所有destination table/dir/local dir的元数据;

9.png

当这一切都执行完了之后,语义解析模块就结束了。

四、生成逻辑执行计划

生成逻辑计划(Logical Plan Gen
Hive
最终生成的MapReduce任务,Map阶段和Reduce阶段均由OperatorTree组成。逻辑操作符,就是在Map阶段或者Reduce阶段完成单一特定的操作。

上一步语义解析,对应的是SemanticAnalyzeranalyzeInternal方法中的genResolvedParseTree(ast, plannerCtx),详见2.5节,那么本章节生成逻辑执行计划就是该方法的genResolvedParseTree的下一个步骤,从 resolved parse tree 生成 op tree

1.png

Operator sinkOp = genOPTree(ast, plannerCtx);

2.png

关键方法:genPlan

3.png

基本的操作符如下所示:

4.png

通过QueryBlock可以生成Operator Tree就是遍历上一个过程中生成的QBQBParseInfo对象的保存语法的属性,包含如下几个步骤:

(1) QB#aliasToSubq => 有子查询,递归调用

(2) QB#aliasToTabs => TableScanOperator

(3) QBParseInfo#joinExpr => QBJoinTree => ReduceSinkOperator + JoinOperator

(4) QBParseInfo#destToWhereExpr => FilterOperator

(5) QBParseInfo#destToGroupby => ReduceSinkOperator + GroupByOperator

(6) QBParseInfo#destToOrderby => ReduceSinkOperator + ExtractOperator

5.png

五、优化逻辑计划(Logical Optinizer


大部分逻辑层优化器通过变换Operator Tree,合并操作符,达到减少MapReduce Job,减少shuffle数据量的目的。

6.png

六、生成物理计划(Physical Plan Gen


在生成相应的查询计划之后,hive需要将逻辑计划转换成一个物理查询计划,这里是将其转换成MapReduce作业

7.png

七、物理任务优化(Physical Optimizer


根据sql语句的不同,mapreduce中包含的操作符也各不相同,但是某些操作符可以进行压缩,合并成一个操作符。接着则启动相应的MapTaskReduceTask

8.png

八、任务执行计划

SemanticAnalyzer.analyzeInternal的第9

9.png

1、构造对应的引擎编译器

TaskCompiler compiler = TaskCompilerFactory.getCompiler(conf, pCtx);

TaskCompilerFactory是工厂类,TaskCompilerFactory. getCompiler是根据hive.execution.engine配置来构造对应的引擎编译器

1.png

2、引擎编译器初始化

compiler.init(queryState, console, db);

3.png

其中TezCompiler和MapReduceCompiler重写了TaskCompiler的init方法,主要是开启相关参数

5.png

3、执行编译

compiler.compile(pCtx, rootTasks, inputs, outputs);

4fetchTask = pCtx.getFetchTask();

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区),文章链接,文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:cloudbbs@huaweicloud.com进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。