作者: 幻好
概述
通过学习 Apache Hive 基础知识和 HIve SQL 后,能够了解到 Hive 是将 SQL 语句通过底层转换生成 MR 程序进行工作,为了能对 Hive 能有一个更加深入的认识,还需要深入理解 Hive SQL 的执行原理。 本文将通过对于 Hive 底层执行原理的深入解读,帮助读者对于 Hive 的作用有更深入的理解。
Hive 底层原理
在使用 Hive 进行日常需求开发使用后,能够大概了解 Hive 是将 SQL 语句通过底层转换生成 MR 程序进行工作,为了能对 Hive 能有一个更加深入的认识,还需要深入理解 Hive SQL 的执行原理。
Hive 执行架构
Hive 是基于 Hadoop 进行交互工作的,所以根据 Hive 的整个工作的流程可以有以下架构图:
核心组件
根据上图,可知 Hive 工作执行中包含以下5个组件:
- UI(用户界面):可看作我们提交 SQL 语句的命令行界面。
- DRIVER(驱动程序):接收查询的组件。该组件实现了会话句柄的概念。
- COMPILER(编译器):负责将 SQL 转化为平台可执行的执行计划。对不同的查询块和查询表达式进行语义分析,并最终借助表和从 META STORE 查找的分区元数据来生成执行计划。
- META STORE(元数据库):存储 Hive 中各种表和分区的所有结构信息。
- EXECUTION ENGINE(执行引擎):负责提交 COMPILER 阶段编译好的执行计划到不同的平台上。
工作流程
结合上图,Hive 的正常工作流程分为以下步骤:
- Step 1:用户通过 UI 界面编写 SQL 提交会调用 DRIVER 的接口;
- Step 2:DRIVER 接收查询任务后,会为查询创建会话句柄,并将查询发送到 COMPILER 生成执行计划;
- Step3:编译器从 META STORE 中获取本次查询所需要的元数据;
- Step 4:查询到对应的元数据,会用于对查询树中的表达式进行类型检查,以及基于查询谓词修建分区;
- Step 5:编译器生成的执行计划是分阶段的 DAG(有向无环图),其中的阶段可能是 map/reduce 作业,也可能是一个元数据或在 HDFS 上的操作。最后将生成的计划发给 DRIVER,如果是 map/reduce 作业,该计划包括 map operator trees 和一个 reduce operator tree,执行引擎将会把这些作业发送给 MapReduce;
- Step 6:执行引擎将这些阶段提交给适当的组件。在每个 task(mapper/reducer) 中,从 HDFS 文件中读取与表或中间输出相关联的数据,并通过相关算子树传递这些数据。最终这些数据通过序列化器写入到一个临时 HDFS 文件中(如果不需要 reduce 阶段,则在 map 中操作)。临时文件用于向计划中后面的 map/reduce 阶段提供数据。
- Step 7:最终的临时文件将移动到相关表的位置,确保不读取脏数据(文件重命名在 HDFS 中是原子操作)。
- Step 8:对于用户的查询,临时文件的内容由执行引擎直接从 HDFS 读取,然后通过 Driver 发送到 UI。
Hive SQL编译原理
Hive 最重要的工作就是将 SQL 语句编译成 MapReduce 的过程,而编译 SQL 的过程是在 COMPILER(编译器)中完成的。编译转换的过程主要分为以下几个阶段:
- 语法解析 :通过语言识别工具 Antlr 定义 SQL 的语法规则,完成 SQL 词法,语法解析,将 SQL 转化为抽象语法树 AST Tree;
- 语义解析 :遍历 AST Tree,抽象出查询的基本组成单元 QueryBlock;
- 生成逻辑执行计划 :遍历 QueryBlock,翻译为执行操作树 OperatorTree;
- 优化逻辑执行计划 :逻辑层优化器进行 OperatorTree 变换,合并 Operator,达到减少 MapReduce Job,减少数据传输及 shuffle 数据量;
- 生成物理执行计划 :遍历 OperatorTree,翻译为 MapReduce 任务;
- 优化物理执行计划 :物理层优化器进行 MapReduce 任务的变换,生成最终的执行计划。
编译案例分析
Hive SQL 编译转换为 MR 程序的过程从概念上理解起来比较抽象,为了更好的理解直接结合一个简单的查询案例进行分析:
select * from hivedb.sys_user where id = '1001';
第一阶段: 语法解析
- 通过 Antlr 定义的 sql 语法规则,将相关 sql 进行词法、语法解析,转化为抽象语法树 AST Tree:
ABSTRACT SYNTAX TREE:
TOK_QUERY
TOK_FROM
TOK_TABREF
TOK_TABNAME
hivedb
sys_user
TOK_INSERT
TOK_DESTINATION
TOK_DIR
TOK_TMP_FILE
TOK_SELECT
TOK_SELEXPR
TOK_ALLCOLREF
TOK_WHERE
=
TOK_TABLE_OR_COL
id
'1001'
-第二阶段:**语义解析 **
- 遍历 AST Tree,抽象出查询的基本组成单元 QueryBlock: AST Tree 生成后由于其复杂度依旧较高,不便于翻译为 mapreduce 程序,需要进行进一步抽象和结构化,形成 QueryBlock。
- QueryBlock 是一条 SQL 最基本的组成单元,包括三个部分:输入源,计算过程,输出。简单来讲一个 QueryBlock 就是一个子查询。
- QueryBlock 的生成过程为一个递归过程,先序遍历 AST Tree ,遇到不同的 Token 节点(理解为特殊标记),保存到相应的属性中。
-第三阶段:**生成逻辑执行计划 ** 遍历 QueryBlock,翻译为执行操作树 OperatorTree:
- Hive 最终生成的 MapReduce 任务,Map 阶段和 Reduce 阶段均由 OperatorTree 组成。
- TableScanOperator
- SelectOperator
- FilterOperator
- JoinOperator
- GroupByOperator
- ReduceSinkOperator
- Operator 在 Map Reduce 阶段之间的数据传递都是一个流式的过程。每一个 Operator 对一行数据完成操作后之后将数据传递给 childOperator 计算。
- 由于 Join/GroupBy/OrderBy 均需要在 Reduce 阶段完成,所以在生成相应操作的 Operator 之前都会先生成一个 ReduceSinkOperator,将字段组合并序列化为 Reduce Key/value, Partition Key。
-第四阶段: 优化逻辑执行计划 Hive 中的逻辑查询优化可以大致分为以下几类:
- 投影修剪
- 推导传递谓词
- 谓词下推
- 将 Select-Select,Filter-Filter 合并为单个操作
- 多路 Join
- 查询重写以适应某些列值的 Join 倾斜
-第五阶段:**生成物理执行计划 ** 生成物理执行计划即是将逻辑执行计划生成的 OperatorTree 转化为 MapReduce Job 的过程,主要分为下面几个阶段:
- 对输出表生成 MoveTask
- 从 OperatorTree 的其中一个根节点向下深度优先遍历
- ReduceSinkOperator 标示 Map/Reduce 的界限,多个 Job 间的界限
- 遍历其他根节点,遇过碰到 JoinOperator 合并 MapReduceTask
- 生成 StatTask 更新元数据
- 剪断 Map 与 Reduce 间的 Operator 的关系
-第六阶段: 优化物理执行计划 Hive 中的物理优化可以大致分为以下几类:
- 分区修剪(Partition Pruning)
- 基于分区和桶的扫描修剪(Scan pruning)
- 如果查询基于抽样,则扫描修剪
- 在某些情况下,在 map 端应用 Group By
- 在 mapper 上执行 Join
- 优化 Union,使 Union 只在 map 端执行
- 在多路 Join 中,根据用户提示决定最后流哪个表
- 删除不必要的 ReduceSinkOperators
- 对于带有 Limit 子句的查询,减少需要为该表扫描的文件数
- 对于带有 Limit 子句的查询,通过限制 ReduceSinkOperator 生成的内容来限制来自 mapper 的输出
- 减少用户提交的 SQL 查询所需的 Tez 作业数量
- 如果是简单的提取查询,避免使用 MapReduce 作业
- 对于带有聚合的简单获取查询,执行不带 MapReduce 任务的聚合
- 重写 Group By 查询使用索引表代替原来的表
- 当表扫描之上的谓词是相等谓词且谓词中的列具有索引时,使用索引扫描
通过以上6个阶段 ,SQL 就被解析映射成了集群上的 MapReduce 任务。
总结
本文主要介绍 Apache Hive 工作的底层原理,通过对于 Hive SQL 执行的工作流程到底层执行过程的解析能够帮助读者更深入的理解 Hive 的作用。