hive源码(二)输出日志、hook处理 Driver类
一:org.apache.hadoop.hive.ql.Driver 类流程
部分方法实体较长、可以直接搜素《《很重要》》 关键字,直接看重要代码
CommandProcessorResponse方法
//重载方法,继续掉用
return run(command, false);
CommandProcessorResponse重载方法
try {
//很重要 下一级代码入口
runInternal(command, alreadyCompiled);
return createProcessorResponse(0);
} catch (CommandProcessorResponse cpr) {
SessionState ss = SessionState.get();
if(ss == null) {
return cpr;
}
//不重要 格式化输出 json 文本格式
MetaDataFormatter mdf = MetaDataFormatUtils.getFormatter(ss.getConf());
if(!(mdf instanceof JsonMetaDataFormatter)) {
return cpr;
}
..........//不重要 异常处理代码
return cpr;
}
runInternal方法
........这一块不想看
if (!alreadyCompiled) { //1949 行
//很重要 sql转成mapreduce过程
compileInternal(command, true);
perfLogger = SessionState.getPerfLogger();
} else {
perfLogger = SessionState.getPerfLogger();
plan.setQueryStartTime(perfLogger.getStartTime(PerfLogger.DRIVER_RUN));
}
.....
try {
//很重要 提交执行mapreduce过程
execute();//2011行
} catch (CommandProcessorResponse cpr) {
rollback(cpr);
throw cpr;
}
compileInternal方法
//不重要 这一块代码 看起来没有任何的含义 +1 -1 lock
//不重要 其实这一块的代码是多个客户端请求的时候,并行解析SQL达到最大值 用的锁
Metrics metrics = MetricsFactory.getInstance();
if (metrics != null) {
metrics.incrementCounter(MetricsConstant.WAITING_COMPILE_OPS, 1);
}
PerfLogger perfLogger = SessionState.getPerfLogger();
perfLogger.PerfLogBegin(CLASS_NAME, PerfLogger.WAIT_COMPILE);
//不重要 代码里面最后一行 将锁lock
final ReentrantLock compileLock = tryAcquireCompileLock(isParallelEnabled,
command);
perfLogger.PerfLogEnd(CLASS_NAME, PerfLogger.WAIT_COMPILE);
if (metrics != null) {
metrics.decrementCounter(MetricsConstant.WAITING_COMPILE_OPS, 1);
}
if (compileLock == null) {
throw createProcessorResponse(ErrorMsg.COMPILE_LOCK_TIMED_OUT.getErrorCode());
}
try {
//很重要 sql转成mapreduce过程
compile(command, true, deferClose);
} catch (CommandProcessorResponse cpr) {
try {
releaseLocksAndCommitOrRollback(false);
} catch (LockException e) {
LOG.warn("Exception in releasing locks. " + org.apache.hadoop.util.StringUtils.stringifyException(e));
}
throw cpr;
} finally {
compileLock.unlock();
}
//不重要 日志相关的选项 忽略
queryDisplay.setPerfLogStarts(QueryDisplay.Phase.COMPILATION, perfLogger.getStartTimes());
queryDisplay.setPerfLogEnds(QueryDisplay.Phase.COMPILATION, perfLogger.getEndTimes());
compile方法
//不重要 日志相关的选项 忽略
PerfLogger perfLogger = SessionState.getPerfLogger(true);
perfLogger.PerfLogBegin(CLASS_NAME, PerfLogger.DRIVER_RUN);
perfLogger.PerfLogBegin(CLASS_NAME, PerfLogger.COMPILE);
lDrvState.stateLock.lock();
try {
lDrvState.driverState = DriverState.COMPILING;
} finally {
lDrvState.stateLock.unlock();
}
//不重要 替换sql中的变量
command = new VariableSubstitution(new HiveVariableSource() {
@Override
public Map<String, String> getHiveVariable() {
return SessionState.get().getHiveVariables();
}
}).substitute(conf, command);
String queryStr = command;
try {
//不重要 Hook操作执行的地方
//有的数仓中利用这个钩子函数 把每个执行的sql记录在mysql 做数仓表血缘图的输入数据
queryStr = HookUtils.redactLogString(conf, command);
} catch (Exception e) {
LOG.warn("WARNING! Query command could not be redacted." + e);
}
//不重要 如果当前操作被杀掉了 就把环境中一些变量销毁掉
checkInterrupted("at beginning of compilation.", null, null);
if (ctx != null && ctx.getExplainAnalyze() != AnalyzeState.RUNNING) {
closeInProcess(false);
}
//不重要 一个sql多个mapreduce的时候 taskid(1.2.3.4.5)不需要重置
if (resetTaskIds) {
TaskFactory.resetId();
}
//不重要 app_771b2de4-da27-4057-8b58-8ea496b914e8 产生、设置SQL
LockedDriverState.setLockedDriverState(lDrvState);
String queryId = queryState.getQueryId();
if (ctx != null) {
setTriggerContext(queryId);
}
this.queryDisplay.setQueryStr(queryStr);
this.queryDisplay.setQueryId(queryId);
LOG.info("Compiling command(queryId=" + queryId + "): " + queryStr);
conf.setQueryString(queryStr);
if (SessionState.get() != null) {
SessionState.get().getConf().setQueryString(queryStr);
SessionState.get().setupQueryCurrentTimestamp();
}
//不重要 hive事务管理(仅支持orc文件格式)
boolean compileError = false;
boolean parseError = false;
try {
if (initTxnMgr != null) {
queryTxnMgr = initTxnMgr;
} else {
queryTxnMgr = SessionState.get().initTxnMgr(conf);
}
if (queryTxnMgr instanceof Configurable) {
((Configurable) queryTxnMgr).setConf(conf);
}
queryState.setTxnManager(queryTxnMgr);
ShutdownHookManager.removeShutdownHook(shutdownRunner);
final HiveTxnManager txnMgr = queryTxnMgr;
shutdownRunner = new Runnable() {
@Override
public void run() {
try {
releaseLocksAndCommitOrRollback(false, txnMgr);
} catch (LockException e) {
LOG.warn("Exception when releasing locks in ShutdownHook for Driver: " +e.getMessage());
}
}
};
ShutdownHookManager.addShutdownHook(shutdownRunner, SHUTDOWN_HOOK_PRIORITY);
checkInterrupted("before parsing and analysing the query", null, null);
if (ctx == null) {
ctx = new Context(conf);
setTriggerContext(queryId);
}
ctx.setHiveTxnManager(queryTxnMgr);
ctx.setStatsSource(statsSource);
ctx.setCmd(command);
ctx.setHDFSCleanup(true);
//不是很重要 将SQL解析成ASTnode 类似于词法解析 不断case when 解析sql 获取所需要的信息就可以了
//里面函数都是一个关键字:一个init和after
//FromClauseParser.g:from后面的sql的匹配规则 SelectClauseParser.g:select后面的sql的匹配规则
//IdentifiersParser.g:函数的解析 HiveParser.g:语法解析的
//利用Antlr将SQL转换为KW_** TOK_**类似的树形结构 下图有样例
perfLogger.PerfLogBegin(CLASS_NAME, PerfLogger.PARSE);
hookRunner.runBeforeParseHook(command);
ASTNode tree;
try {
tree = ParseUtils.parse(command, ctx);
} catch (ParseException e) {
parseError = true;
throw e;
} finally {
hookRunner.runAfterParseHook(command, parseError);
}
perfLogger.PerfLogEnd(CLASS_NAME, PerfLogger.PARSE);
hookRunner.runBeforeCompileHook(command);
SessionState.get().getCurrentFunctionsInUse().clear();
perfLogger.PerfLogBegin(CLASS_NAME, PerfLogger.ANALYZE);
//不重要 刷新元数据信息
Hive.get().getMSC().flushCache();
backupContext = new Context(ctx);
boolean executeHooks = hookRunner.hasPreAnalyzeHooks();
//不重要 继续Hook
HiveSemanticAnalyzerHookContext hookCtx = new HiveSemanticAnalyzerHookContextImpl();
if (executeHooks) {
hookCtx.setConf(conf);
hookCtx.setUserName(userName);
hookCtx.setIpAddress(SessionState.get().getUserIpAddress());
hookCtx.setCommand(command);
hookCtx.setHiveOperation(queryState.getHiveOperation());
tree = hookRunner.runPreAnalyzeHooks(hookCtx, tree);
}
//很重要 决定面下面使用什么类继续运行 下一节继续写
BaseSemanticAnalyzer sem = SemanticAnalyzerFactory.get(queryState, tree);
if (!retrial) {
openTransaction();
generateValidTxnList();
}
//很重要 将语法树转换为物理执行计划
sem.analyze(tree, ctx);
if (executeHooks) {
hookCtx.update(sem);
hookRunner.runPostAnalyzeHooks(hookCtx, sem.getAllRootTasks());
}
LOG.info("Semantic Analysis Completed (retrial = {})", retrial);
// Retrieve information about cache usage for the query.
if (conf.getBoolVar(HiveConf.ConfVars.HIVE_QUERY_RESULTS_CACHE_ENABLED)) {
cacheUsage = sem.getCacheUsage();
}
sem.validate();
perfLogger.PerfLogEnd(CLASS_NAME, PerfLogger.ANALYZE);
checkInterrupted("after analyzing query.", null, null);
//不重要 这一块见名知意 返回数据的列名、查询记录
schema = getSchema(sem, conf);
plan = new QueryPlan(queryStr, sem, perfLogger.getStartTime(PerfLogger.DRIVER_RUN), queryId,
queryState.getHiveOperation(), schema);
conf.set("mapreduce.workflow.id", "hive_" + queryId);
conf.set("mapreduce.workflow.name", queryStr);
if (plan.getFetchTask() != null) {
plan.getFetchTask().initialize(queryState, plan, null, ctx.getOpContext());
}
...... 后面不重要了
}
二:org.apache.hadoop.hive.ql.parse.BaseSemanticAnalyzer 类
sem.analyze(tree, ctx);
上面返回的是CalcitePlanner
但是CalcitePlanner并没有覆盖em.analyze(tree, ctx); 方法的
所以最终执行的还是BaseSemanticAnalyzer 的analyze 方法
analyze方法
initCtx(ctx);
init(true);
analyzeInternal(ast);//BaseSemanticAnalyzer方法是接口,所以执行CalcitePlanner类的 analyzeInternal 方法
public void analyzeInternal(ASTNode ast) throws SemanticException {//其实调用的是父类的方法
if (runCBO) {
super.analyzeInternal(ast, new PlannerContextFactory() {
@Override
public PlannerContext create() {
return new PreCboCtx();
}
});
} else {
super.analyzeInternal(ast);
}
}
所以直接调转:SemanticAnalyzer类里面
搬砖多年终不得要领,遂载源码看之望得真经。