最近整理了下自己以前的笔记,对Hive整体运行流程做了一些总结。
一、Hive简介和原理
1)Hive起源
Hive起源于FACEBOOK,由FACEBOOK的几名员工共同提出。最早提出的Hive概念来源于以下论文Hive——A Warehousing Solution Over A Map-Reduce Framework(论文见目录)。它是基于MapReduce框架的数据仓库的解决方案。主要解决了并行执行数据分析和机器查询的效率。Hive已经成为最流行的基于Hadoop的数据存储框架和数据分析框架,以及事实的大数据SQL标准和元数据标准;是Hadoop生态栈中最主要的基础架构。
Hive主要是面向非专业人士,尤其是大数据分析师最好的工具。Hive的提供的功能非常健全,是效率最高、便于使用的作业提交工具。
简化后的HIVE架构:
2)HIVE工作流
HIVE的最小处理单元是OPERATORS ,OPERATORS(该术语来源于关系型数据中的术语,操作符。)可以是运行在集群的MAP任务,也可以是运行在集群的REDUCE任务,或者是运行在本地对于HDFS的操作;这些都会被抽象成OPERATORS。
HIVE的COMPILER的作用是:把一条HIVE SQL,转换为一个OPERATORS的 图(这里大部分情况下会是一个树,树状结构)。
HIVE中OPERATORS的种类:
OPERATORS | FUNCTION DESCRIBLE | 缩写 |
TableScanOperator | 从表中读取数据 | TS |
ReduceSinkOperator | 把数据发送给Reduce端作聚合操作 | RS |
JoinOperator | 作两个表的关联操作Join | JOIN |
SelectOperator(投影) | 选择部分列并输出 | SEL |
FileSinkOperator | 建立结果数据并发送给文件 | FS |
FilterOperator | 过滤输入数据 | FIL |
GroupByOperator | 对数据作Group By操作 | GBY |
MapJoinOperator | 在内存中作大表和小表的关联操作 | MapJOIN |
LimitOperator | 作limit,返回一定条目的数据 | LIMIT |
UnionOperator | 作union | UNION |
在较老的版本中,HIVE通常是一个MAPREDUCE作业(新版本可能是TEZ)。MAPPER是EXECMAPPER,REDUCER是EXECREDUCE。DRICVER实际上有多种处理模式,例如本地模式和分布式模式。
在HIVE中最核心的模块就是COMPILER,它负责将一个字符串(HIVESQL),转化成一个执行计划。
简化后的执行流程图:
为了说明上述执行流程,需要辅助一个简单的Hive查询例子:
案例1-1:Hive执行以下语句:
INSERT OVERWRITE TABLE access_log_temp2
SELECT a.user, a.prono, a.maker, a.price FROM access_log_hbase a JOIN product_hbase p
ON (a.prono = p.prono)
3)具体执行流程:
(1)Parser生成AST
HQL生成的语法解析树(AST):
HIVE主要是利用UNDERLER处理得到上面的抽象语法树(这棵抽象语法树主要是根据underller的分词规则来确定根结点和左右孩子的,Hive的这部分处理是利用underller进行的)。
(2)Semantic Analyzer生成Query Block(QB)
Tip:
第二步会将抽象语法树按照QB的方式转换为由QB组成的树。通常情况下,每一个From子句会生成一个QB(Query Block)。通过查看具体有多少个QB就可查看Hive sql语句最终优化成多少个MapReduce的作业。
QB是由两个子数据结构组成即MetaData(元数据信息)和ParseInfo。语义分析的主要过程是向QB中填充元数据,另一个过程则是摘取AST的子节点,存储在ParseInfo中。Semantic Analyzer过程主要是经过以上两个过程,然后把QB关联起来形成一个由QB构成的图.
Logical Plan generator会将上一步生成的Query Block按照前文讲的几种的Operator,转换为Operator Tree。
由于Hive查询语句会将最后的查询结果写入到表access_log_temp2中,所以QB的MetaData中的Name To Destination…会转化为Operator Tree中的FileSinkOperator(文件下沉)。
最后会生成如下的OP Tree:
Logical Plan Generator (Result)
这里就得到了一个有向无环图(DAG),当在传统数据库中会对上图进行优化然后执行;而在Hadoop集群中这张图是不行的,因为在集群中执行需要对这张图进行优化、切片,分布式化转化为MapReduce作业然后执行。
(4)LOGICAL OPTIMIZER
首先,Hive会对上图进行优化,重绘。
这里需要介绍Hive中的优化器如下表:
优化器 | 简介 |
LineageGenerator | 各个Operator血缘情况的设定,优化Operator的血缘关系 |
ColumnPruner | 列剪裁优化器 |
Predicate PushDown | 谓词下推优化器,将条件推到特定位置(where条件) |
PartitionPruner | 分区裁剪条件优化器 |
PartitionCondition Remove | PartitionPruner消除无用的分支的优化器 |
GroupByOptimizer | Group by优化两阶段中的Map端预阶段聚合的优化器 |
SamplePruner | 抽样优化器,降低抽样的数据量 |
MapJoinProcessor | 在特定的情况下,把JoinOperator改写成MapJoinOperator的 优化器 |
BucketMapJoin Optimizer | 对Bucket表做MapJoin的优化器 |
SortedMergeBucket MapJoinOptimizer | 对SortedMergeBucket进行MapJoin的优化器 |
优化器 | 简介 |
UnionProcessor | 识别两边的子查询是否都是Map-Only的 |
JoinReader | /*+ STREAMTABLE(A) */ 指定Join的 |
ReduceSink DeDuplication | 如果两个ReduceSink的操作符共享分区和排列顺序也一样,此时就可以将这两个表放在一起进行ReduceSink操作,提高效率。 |
上表中的优化器都是逻辑优化器。为了进一步介绍这些优化器,这里举个例子来进行说明。
案例1-2 LOGICALOPTIMIZER(PREDIC PUSHDOWN)
INSERTOVERWRITE TABLE access_log_temp2
SELECTa.user, a.prono, a.maker, a.price FROM access_log_hbase a JOIN product_hbase p
ON(a.prono = p.prono) where p.mark = “honda”;
此时,原来的逻辑计划图就会增加一个节点,如下图:
这是一个未进行优化的OP Tree,此时会存在一个问题:当access_log_hbase和product_hbase两个表的数据量非常大的时候,第三步进行的Join操作,就会产生海量的数之前就先进性过滤出”honda”的数据,此时向Reduce端发送的数据会变得很少,Join时数据也会变得很少,从而大幅度提高Hive SQL的执行效率。这一个优化的过程就叫做谓词下推,即把where选择过滤操作下推到合适的步骤进行。
进过谓词下推优化器优化后的OP Tree如下图:
(5)PHYSICAL PLAN GENERATOR
物理执行计划会将OP Tree切成若干个Task,而一个Task就对应着一个MapReduce作业。由于案例中只有一个QB,所以最后会生成一个Task即一个MapReduce作业。
当DAG中存在Join或者Group By时,会在这个Operator之前的节点操作会在Map中执行,而这个Operator之后的节点都会在Reduce执行。而两者会通过ReduceSinkOperator操作来连接,这个操作符会将Map端的数据发送到Reduce端。
PHYSICAL PLAN GENERATOR (Result)
这样就将一个OP Tree转换为了一个Map-Red的作业。但是这个Map-Red作业还需要进行物理级别的优化。
(6)PHYSICAL OPTIMIZER
物理优化器
物理优化的类主要在Hive的org/apache/hadoop/hive/ql/optimizer/physical/包内。
主要有以下几个物理优化器。
i)以MapJoinResolver为例,介绍物理优化
请看下图(前例优化后的物理执行计划)
1.此时,只有Map过程,即在本地进行小表的压缩、上传;在服务端读大表然后进行关联。
2.在进行Join时,一般用户都会使用MapJoin来手动指令Select Map Join来进行操作。但是,如果用在进行Join时,用户并不知道哪个表大哪个表小也不知道该不该使用Map Join,此时使用CommonJoinResolver则会解决这一问题。首先不管表大还是表小,他都会对Task Tree做一定的优化,会在Join 的Map-Red Task之前加上一个conditional Task。当作业到执行到Conditional Task时,会在执行Join的Map-Red作业之前对表的大小先进行判断;如果表是大表则进行Join操作;当表是小表时则进行MapJoin操作。这样用户就不必关心表大还是小,也不需要关心具体执行过程。
这里把原来的物理计划中Join换成了MapJoin,从而避免了Map-Red过程,转换成为了只有Map过程的Task。从而避免了数据在各个节点之间的传输,大幅度提高了Task的执行效率。但是,这个物理执行计划的DAG是不能直接执行的,这里无法确定那个是大表,从而无法确定那个表要读入到内存。而物理优化器的作用就是对一个Task Tree(DAG)进行优化,选出读入内存的表,进行合理的优化提高执行效率。
具体转换过程见下图:
转换后Task Tree是由两个Task组成的DAG。第一个Task是在本地进行MapredLocalTask,首先会在本地读小表,然后把小表通过HashTableSinkOperator转换为一个Hashtable,然后打包、压缩,通过DistrubuteChach机制将其上传到服务器上。
在服务器上会先去读取大表,然后MapJoinOperator会去下载小表,然后将其读入到内存中,然后再内存中进行MapJoin。由于Map Join是在内存中进行的,并且此时只是均匀的去读一个大表,每个进程只读64M,可以在一个均匀的时间内完成Select并输出。此时只需要客户端都一个小表,服务器都一个大表完成MapJoin操作。
物理优化器的作用主要是改写Task Tree,根据不同的情况有不同的给些方式。详细内容请查看物理优化部分。
最后,来整体做个总结。
Hive 整体架构:
执行流程回顾:
补充:如何查看执行计划?
只需要在Hive中执行的SQL语句前面加上EXPLAN语句。
**tip**
这里的总结都是我参加hive2.1培训的笔记(小象学院)。
虽然大体上对整个hive的执行流程做了个总结吧。但是对于初学者还是很难全部理解的,后面我会整理一些更细节的hive各个模块的具体理解内容。