转载:
http://blog.csdn.net/wf1982/article/details/9122543
Hive SQL解析/执行计划生成流程分析
近在研究Impala,还是先回顾下Hive的SQL执行流程吧。
Hive有三种用户接口:
cli (Command line interface) bin/hive或bin/hive –service cli 命令行方式(默认)
hive-server/hive-server2 bin/hive –service hiveserver 或bin/hive –service hiveserver2 通过JDBC/ODBC和Thrift访问(Impala通过这种方式借用hive-metastore)
hwi (Hive web interface) bin/hive –service hwi 通过浏览器访问
在hive shell中输入“show tables;”实际执行的是:
1
bin/hadoop
jar hive/lib/hive-cli-0.9.0.jar
org.apache.hadoop.hive.cli.CliDriver -e 'SHOW
TABLES;'
CLI入口函数:cli.CliDriver.main()
读入参数->建立SessionState并导入配置->处理输入文件中指令CliDriver.processFile();或交互型指令CliDriver.processLine()->解析输入CliDriver.processCmd()
(1) 如果是quit或者exit,退出
(2) 以source开头的,读取外部文件并执行文件中的HiveQL
(3) !开头的命令,执行操作系统命令(如!ls,列出当前目录的文件信息)
(4) list,列出jar/file/archive
(5) 其他命令,则生成调用相应的CommandProcessor处理,进入CliDriver.processLocalCmd()
下面看看CliDriver.processLocalCmd()这个函数:
set/dfs/add/delete指令交给指定的CommandProcessor处理,其余的交给org.apache.hadoop.hive.ql.Driver.run()处理
org.apache.hadoop.hive.ql.Driver类是查询的起点,run()方法会先后调用compile()和execute()两个函数来完成查询,所以一个command的查询分为compile和execute两个阶段。
Compile
(1)利用antlr生成的HiveLexer.java和HiveParser.java类,将HiveQL转换成抽象语法树(AST)。
首先使用antlr工具将srcqlsrcjavaorgapachehadoophiveqlparsehive.g编译成以下几个文件:HiveParser.java, Hive.tokens, Hive__.g, HiveLexer.java
HiveLexer.java和HiveParser.java分别是词法和语法分析类文件,Hive__.g是HiveLexer.java对应的词法分析规范,Hive.tokens定义了词法分析后所有的token。
然后沿着“Driver.compile()->ParseDriver.parse(command, ctx)->HiveParserX.statement()->antlr中的API”这个调用关系把输入的HiveQL转化成ASTNode类型的语法树。HiveParserX是由antlr生成的HiveParser类的子类。
(2)利用对应的SemanticAnalyzer类,将AST树转换成Map-reduce task。主要分为三个步骤:
a) AST -> operator DAG
b) optimize operator DAG
c) oprator DAG -> Map-reduce task
首先接着上一步生成的语法树ASTNode, SemanticAnalyzerFactory会根据ASTNode的token类型生成不同的SemanticAnalyzer (所有这些SemanticAnalyzer都继承自BaseSemanticAnalyzer)
1) ExplainSemanticAnalyzer
2) LoadSemanticAnalyzer
3) ExportSemanticAnalyzer
4) DDLSemanticAnalyzer
5) FunctionSemanticAnalyzer
6) SemanticAnalyzer
然后调用BaseSemanticAnalyzer.analyze()->BaseSemanticAnalyzer. analyzeInternal()。
下面以最常见的select * from table类型的查询为例,进入的子类是SemanticAnalyzer. analyzeInternal(),这个函数的逻辑如下:
1) doPhase1():将sql语句中涉及到的各种信息存储起来,存到QB中去,留着后面用。
2) getMetaData():获取元数据信息,主要是sql中涉及到的 表 和 元数据 的关联
3) genPlan():生成operator tree/DAG
4) optimize:优化,对operator tree/DAG 进行一些优化操作,例如列剪枝等(目前只能做rule-based optimize,不能做cost-based optimize)
5) genMapRedTasks():将operator tree/DAG 通过一定的规则生成若干相互依赖的MR任务
Execute
将Compile阶段生成的task信息序列化到plan.xml,然后启动map-reduce,在configure时反序列化plan.xml
实例分析:
在hive中有这样一张表:
uid
fruit_name
count
a
apple
5
a
orange
3
a
apple
2
b
banana
1
执行如下的查询:
1
SELECT
uid, SUM(count)
FROM
logs GROUP
BY
uid
通过explain命令可以查看执行计划:
1
EXPLAIN
SELECT
uid, SUM(count)
FROM
logs GROUP
BY
uid;
依照hive.g的语法规则,生成AST如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ABSTRACT
SYNTAX TREE:
(
TOK_QUERY
(TOK_FROM
(TOK_TABREF (TOK_TABNAME logs)))
(
TOK_INSERT
(TOK_DESTINATION
(TOK_DIR TOK_TMP_FILE))
(
TOK_SELECT
(TOK_SELEXPR
(TOK_TABLE_OR_COL uid))
(TOK_SELEXPR
(TOK_FUNCTION sum
(TOK_TABLE_OR_COL count)))
)
(TOK_GROUPBY
(TOK_TABLE_OR_COL uid))
)
)
生成的执行计划operator tree/DAG如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
STAGE
DEPENDENCIES:
Stage-1
is
a root stage
Stage-0
is
a root stage
STAGE
PLANS:
Stage:
Stage-1
Map
Reduce
Alias
-> Map Operator Tree:
logs
TableScan
// 扫描表
alias:
logs
Select
Operator //选择字段
expressions:
expr:
uid
type:
string
expr:
count
type:
int
outputColumnNames:
uid, count
Group
By
Operator //在map端先做一次聚合,减少shuffle数据量
aggregations:
expr:
sum(count)
//聚合函数
bucketGroup:
false
keys:
expr:
uid
type:
string
mode:
hash
outputColumnNames:
_col0, _col1
Reduce
Output
Operator //输出key,value给reduce
key
expressions:
expr:
_col0
type:
string
sort
order:
+
Map-reduce
partition columns:
expr:
_col0
type:
string
tag:
-1
value
expressions:
expr:
_col1
type:
bigint
Reduce
Operator Tree:
Group
By
Operator
aggregations:
expr:
sum(VALUE._col0)
//聚合
bucketGroup:
false
keys:
expr:
KEY._col0
type:
string
mode:
mergepartial
outputColumnNames:
_col0, _col1
Select
Operator //选择字段
expressions:
expr:
_col0
type:
string
expr:
_col1
type:
bigint
outputColumnNames:
_col0, _col1
File
Output
Operator //输出到文件
compressed:
false
GlobalTableId:
0
table:
input
format: org.apache.hadoop.mapred.TextInputFormat
output
format: org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat
Stage:
Stage-0
Fetch
Operator
limit:
-1
Hive优化策略:
1. 去除查询中不需要的column
2. Where条件判断等在TableScan阶段就进行过滤
3. 利用Partition信息,只读取符合条件的Partition
4. Map端join,以大表作驱动,小表载入所有mapper内存中
5. 调整Join顺序,确保以大表作为驱动表
6. 对于数据分布不均衡的表Group by时,为避免数据集中到少数的reducer上,分成两个map-reduce阶段。第一个阶段先用Distinct列进行shuffle,然后在reduce端部分聚合,减小数据规模,第二个map-reduce阶段再按group-by列聚合。
7. 在map端用hash进行部分聚合,减小reduce端数据处理规模。
参考文献:
http://fatkun.com/2013/01/hive-group-by.html
更多 0
http://blog.csdn.net/wf1982/article/details/9122543
Hive SQL解析/执行计划生成流程分析
近在研究Impala,还是先回顾下Hive的SQL执行流程吧。
Hive有三种用户接口:
cli (Command line interface) bin/hive或bin/hive –service cli 命令行方式(默认)
hive-server/hive-server2 bin/hive –service hiveserver 或bin/hive –service hiveserver2 通过JDBC/ODBC和Thrift访问(Impala通过这种方式借用hive-metastore)
hwi (Hive web interface) bin/hive –service hwi 通过浏览器访问
在hive shell中输入“show tables;”实际执行的是:
1
bin/hadoop
jar hive/lib/hive-cli-0.9.0.jar
org.apache.hadoop.hive.cli.CliDriver -e 'SHOW
TABLES;'
CLI入口函数:cli.CliDriver.main()
读入参数->建立SessionState并导入配置->处理输入文件中指令CliDriver.processFile();或交互型指令CliDriver.processLine()->解析输入CliDriver.processCmd()
(1) 如果是quit或者exit,退出
(2) 以source开头的,读取外部文件并执行文件中的HiveQL
(3) !开头的命令,执行操作系统命令(如!ls,列出当前目录的文件信息)
(4) list,列出jar/file/archive
(5) 其他命令,则生成调用相应的CommandProcessor处理,进入CliDriver.processLocalCmd()
下面看看CliDriver.processLocalCmd()这个函数:
set/dfs/add/delete指令交给指定的CommandProcessor处理,其余的交给org.apache.hadoop.hive.ql.Driver.run()处理
org.apache.hadoop.hive.ql.Driver类是查询的起点,run()方法会先后调用compile()和execute()两个函数来完成查询,所以一个command的查询分为compile和execute两个阶段。
Compile
(1)利用antlr生成的HiveLexer.java和HiveParser.java类,将HiveQL转换成抽象语法树(AST)。
首先使用antlr工具将srcqlsrcjavaorgapachehadoophiveqlparsehive.g编译成以下几个文件:HiveParser.java, Hive.tokens, Hive__.g, HiveLexer.java
HiveLexer.java和HiveParser.java分别是词法和语法分析类文件,Hive__.g是HiveLexer.java对应的词法分析规范,Hive.tokens定义了词法分析后所有的token。
然后沿着“Driver.compile()->ParseDriver.parse(command, ctx)->HiveParserX.statement()->antlr中的API”这个调用关系把输入的HiveQL转化成ASTNode类型的语法树。HiveParserX是由antlr生成的HiveParser类的子类。
(2)利用对应的SemanticAnalyzer类,将AST树转换成Map-reduce task。主要分为三个步骤:
a) AST -> operator DAG
b) optimize operator DAG
c) oprator DAG -> Map-reduce task
首先接着上一步生成的语法树ASTNode, SemanticAnalyzerFactory会根据ASTNode的token类型生成不同的SemanticAnalyzer (所有这些SemanticAnalyzer都继承自BaseSemanticAnalyzer)
1) ExplainSemanticAnalyzer
2) LoadSemanticAnalyzer
3) ExportSemanticAnalyzer
4) DDLSemanticAnalyzer
5) FunctionSemanticAnalyzer
6) SemanticAnalyzer
然后调用BaseSemanticAnalyzer.analyze()->BaseSemanticAnalyzer. analyzeInternal()。
下面以最常见的select * from table类型的查询为例,进入的子类是SemanticAnalyzer. analyzeInternal(),这个函数的逻辑如下:
1) doPhase1():将sql语句中涉及到的各种信息存储起来,存到QB中去,留着后面用。
2) getMetaData():获取元数据信息,主要是sql中涉及到的 表 和 元数据 的关联
3) genPlan():生成operator tree/DAG
4) optimize:优化,对operator tree/DAG 进行一些优化操作,例如列剪枝等(目前只能做rule-based optimize,不能做cost-based optimize)
5) genMapRedTasks():将operator tree/DAG 通过一定的规则生成若干相互依赖的MR任务
Execute
将Compile阶段生成的task信息序列化到plan.xml,然后启动map-reduce,在configure时反序列化plan.xml
实例分析:
在hive中有这样一张表:
uid
fruit_name
count
a
apple
5
a
orange
3
a
apple
2
b
banana
1
执行如下的查询:
1
SELECT
uid, SUM(count)
FROM
logs GROUP
BY
uid
通过explain命令可以查看执行计划:
1
EXPLAIN
SELECT
uid, SUM(count)
FROM
logs GROUP
BY
uid;
依照hive.g的语法规则,生成AST如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ABSTRACT
SYNTAX TREE:
(
TOK_QUERY
(TOK_FROM
(TOK_TABREF (TOK_TABNAME logs)))
(
TOK_INSERT
(TOK_DESTINATION
(TOK_DIR TOK_TMP_FILE))
(
TOK_SELECT
(TOK_SELEXPR
(TOK_TABLE_OR_COL uid))
(TOK_SELEXPR
(TOK_FUNCTION sum
(TOK_TABLE_OR_COL count)))
)
(TOK_GROUPBY
(TOK_TABLE_OR_COL uid))
)
)
生成的执行计划operator tree/DAG如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
STAGE
DEPENDENCIES:
Stage-1
is
a root stage
Stage-0
is
a root stage
STAGE
PLANS:
Stage:
Stage-1
Map
Reduce
Alias
-> Map Operator Tree:
logs
TableScan
// 扫描表
alias:
logs
Select
Operator //选择字段
expressions:
expr:
uid
type:
string
expr:
count
type:
int
outputColumnNames:
uid, count
Group
By
Operator //在map端先做一次聚合,减少shuffle数据量
aggregations:
expr:
sum(count)
//聚合函数
bucketGroup:
false
keys:
expr:
uid
type:
string
mode:
hash
outputColumnNames:
_col0, _col1
Reduce
Output
Operator //输出key,value给reduce
key
expressions:
expr:
_col0
type:
string
sort
order:
+
Map-reduce
partition columns:
expr:
_col0
type:
string
tag:
-1
value
expressions:
expr:
_col1
type:
bigint
Reduce
Operator Tree:
Group
By
Operator
aggregations:
expr:
sum(VALUE._col0)
//聚合
bucketGroup:
false
keys:
expr:
KEY._col0
type:
string
mode:
mergepartial
outputColumnNames:
_col0, _col1
Select
Operator //选择字段
expressions:
expr:
_col0
type:
string
expr:
_col1
type:
bigint
outputColumnNames:
_col0, _col1
File
Output
Operator //输出到文件
compressed:
false
GlobalTableId:
0
table:
input
format: org.apache.hadoop.mapred.TextInputFormat
output
format: org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat
Stage:
Stage-0
Fetch
Operator
limit:
-1
Hive优化策略:
1. 去除查询中不需要的column
2. Where条件判断等在TableScan阶段就进行过滤
3. 利用Partition信息,只读取符合条件的Partition
4. Map端join,以大表作驱动,小表载入所有mapper内存中
5. 调整Join顺序,确保以大表作为驱动表
6. 对于数据分布不均衡的表Group by时,为避免数据集中到少数的reducer上,分成两个map-reduce阶段。第一个阶段先用Distinct列进行shuffle,然后在reduce端部分聚合,减小数据规模,第二个map-reduce阶段再按group-by列聚合。
7. 在map端用hash进行部分聚合,减小reduce端数据处理规模。
参考文献:
http://fatkun.com/2013/01/hive-group-by.html
更多 0