目录
hive简介
什么是Hive?
- 数据仓库:存储、查询、分析大规模数据
- SQL语言:简单易用的类SQL查询语言
- 编程模型:允许开发者自定义UDF、Transform、Mapper、Reducer,来更简单地完成复杂MapReduce无法完成的工作
- 数据格式:处理Hadoop上任意数据格式的数据,或者使用优化的格式存储Hadoop上的数据,RCFile,ORCFile,Parquest
- 数据服务:HiveServer2,多种API访问Hadoop上的数据,JDBC,ODBC
- 元数据服务:数据什么样,数据在哪里,Hadoop上的唯一标准
hive架构图
hive cli架构图
hive源码结构
hive三大核心模块
Query Processor
:查询处理工具,源码ql包SerDe
:序列化与反序列化器,源码serde包MetaStore
:元数据存储及服务,源码metastore包
hive执行命令入口
入口为:/bin/cli.sh
一个查询(假设通过CLI入口)直到获取最终结果,Hive内部的执行流程主要包括:
- CLI 获取用户查询,解析用户输入的命令,提交给Driver;
- Driver 结合编译器(COMPILER)和元数据库(METASTORE),对用户查询进行编译解析;
- 根据解析结果(查询计划)生成MR任务提交给Hadoop执行;
- 获取最终结果;
接收命令的入口函数
CliDriver类的执行入口,cli.CliDriver.main()
函数,创建CliDriver实例,接受用户输入参数,开始运行。
public static void main(String[] args) throws Exception {
int ret = new CliDriver().run(args);
System.exit(ret);
}
调用cli.CliDriver.run()
方法
方法返回return executeDriver(ss, conf, oproc);
调用executeDriver,在executeDriver中对应上边提到的三种情况:
- 一种是hive -e执行sql,此时ss.execString非空,执行完进程退出;
- 一种是hive -f执行sql文件,此时ss.fileName非空,执行完进程退出;
- 一种是hive交互式执行sql,此时会不断读取reader.readLine,然后执行失去了并输出结果;
executeDriver
调用 cli.processLine()
或者cli.processFile()
if (ss.execString != null) {
int cmdProcessStatus = cli.processLine(ss.execString);
return cmdProcessStatus;
}
try {
if (ss.fileName != null) {
return cli.processFile(ss.fileName);
} catch (FileNotFoundException e) {
System.err.println("Could not open input file for reading. (" + e.getMessage() + ")");
return 3;
}
cli.processLine()后面会调用cli.processFile()方法
调用CliDriver.processCmd()
方法
CliDriver.processCmd()
方法根据指令不同进行对应的操作,指令分为四种:
- quit or exit系统正常退出
- !开头的命令行执行操作系统命令
- source开头的,读取外部文件并执行文件中的命令list列出jar file archive
- 其他命令提交给Commandprocess,进行命令的预处理
调用processLocalCmd()方法
//CommandProcessorFactory 根据用户指令生成的tokens和配置文件,返回CommandProcessor的一个具体实现
CommandProcessor proc = CommandProcessorFactory.get(tokens, (HiveConf) conf);
//提交用户的cmd到指定的CommandProcessor,并获取结果。
ret = processLocalCmd(cmd, proc, ss);
很核心的一个方法,提交用户的cmd到指定的CommandProcessor
,并获取结果.
调用Driver.run()方法
在processLocalCmd()中调用Driver.run()方法去执行sql
qp.setTryCount(tryCount);
//driver实例运行用户指令,获取运行结果响应码
ret = qp.run(cmd).getResponseCode();
if (ret != 0) {
qp.close();
return ret;
}
// 统计指令的运行时间
long end = System.currentTimeMillis();
double timeTaken = (end - start) / 1000.0;
ArrayList<String> res = new ArrayList<String>();
//打印查询结果的列名称
printHeader(qp, out);
// 打印查询结果
int counter = 0;
try {
if (out instanceof FetchConverter) {
((FetchConverter)out).fetchStarted();
}
while (qp.getResults(res)) {
for (String r : res) {
out.println(r);
}
counter += res.size();
res.clear();
if (out.checkError()) {
break;
}
}
} catch (IOException e) {
console.printError("Failed with exception " + e.getClass().getName() + ":"
+ e.getMessage(), "\n"
+ org.apache.hadoop.util.StringUtils.stringifyException(e));
ret = 1;
}
//关闭结果
int cret = qp.close();
if (ret == 0) {
ret = cret;
}
if (out instanceof FetchConverter) {
((FetchConverter)out).fetchFinished();
}
console.printInfo("Time taken: " + timeTaken + " seconds" +
(counter == 0 ? "" : ", Fetched: " + counter + " row(s)"));
} else {
//如果proc不是Driver,也就是用户执行的是非SQL查询操作,直接执行语句,不自信FetchResult的操作
String firstToken = tokenizeCmd(cmd.trim())[0];
String cmd_1 = getFirstCmd(cmd.trim(), firstToken.length());
if (ss.getIsVerbose()) {
ss.out.println(firstToken + " " + cmd_1);
}
CommandProcessorResponse res = proc.run(cmd_1);
if (res.getResponseCode() != 0) {
ss.out.println("Query returned non-zero code: " + res.getResponseCode() +
", cause: " + res.getErrorMessage());
}
ret = res.getResponseCode();
}
}
} catch (CommandNeedRetryException e) {
//如果执行过程中出现异常,修改needRetry标志,下次循环是retry。
console.printInfo("Retry query with a different approach...");
tryCount++;
needRetry = true;
}
} while (needRetry);
之后进入Driver类对sql进行处理,包括编译、解析、优化、执行。