作者: 幻好

概述

通过学习 Apache Hive 基础知识和 HIve SQL 后,能够了解到 Hive 是将 SQL 语句通过底层转换生成 MR 程序进行工作,为了能对 Hive 能有一个更加深入的认识,还需要深入理解 Hive SQL 的执行原理。 本文将通过对于 Hive 底层执行原理的深入解读,帮助读者对于 Hive 的作用有更深入的理解。

hive源码调试运行 hive源码解析_hadoop

Hive 底层原理

在使用 Hive 进行日常需求开发使用后,能够大概了解 Hive 是将 SQL 语句通过底层转换生成 MR 程序进行工作,为了能对 Hive 能有一个更加深入的认识,还需要深入理解 Hive SQL 的执行原理。

Hive 执行架构

Hive 是基于 Hadoop 进行交互工作的,所以根据 Hive 的整个工作的流程可以有以下架构图:

hive源码调试运行 hive源码解析_java_02

核心组件

根据上图,可知 Hive 工作执行中包含以下5个组件:

  1. UI(用户界面):可看作我们提交 SQL 语句的命令行界面。
  2. DRIVER(驱动程序):接收查询的组件。该组件实现了会话句柄的概念。
  3. COMPILER(编译器):负责将 SQL 转化为平台可执行的执行计划。对不同的查询块和查询表达式进行语义分析,并最终借助表和从 META STORE 查找的分区元数据来生成执行计划。
  4. META STORE(元数据库):存储 Hive 中各种表和分区的所有结构信息。
  5. 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(编译器)中完成的。编译转换的过程主要分为以下几个阶段:

hive源码调试运行 hive源码解析_hive源码调试运行_03

  1. 语法解析 :通过语言识别工具 Antlr 定义 SQL 的语法规则,完成 SQL 词法,语法解析,将 SQL 转化为抽象语法树 AST Tree;
  2. 语义解析 :遍历 AST Tree,抽象出查询的基本组成单元 QueryBlock;
  3. 生成逻辑执行计划 :遍历 QueryBlock,翻译为执行操作树 OperatorTree;
  4. 优化逻辑执行计划 :逻辑层优化器进行 OperatorTree 变换,合并 Operator,达到减少 MapReduce Job,减少数据传输及 shuffle 数据量;
  5. 生成物理执行计划 :遍历 OperatorTree,翻译为 MapReduce 任务;
  6. 优化物理执行计划 :物理层优化器进行 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 的作用。