hive源码(六)OperatorTree转换为物理执行计划,物理执行计划优化
OperatorTree转换为物理执行计划,物理执行计划优化
代码入口
TaskCompiler compiler = TaskCompilerFactory.getCompiler(conf, pCtx);
compiler.init(queryState, console, db);
//代码入口
compiler.compile(pCtx, rootTasks, inputs, outputs);
fetchTask = pCtx.getFetchTask();
compile方法详解
public void compile(final ParseContext pCtx,
final List<Task<? extends Serializable>> rootTasks,
final HashSet<ReadEntity> inputs, final HashSet<WriteEntity> outputs) throws SemanticException {
......
//reduce以后,数据从哪读取 临时目录,limit个数
List<LoadTableDesc> loadTableWork = pCtx.getLoadTableWork();
List<LoadFileDesc> loadFileWork = pCtx.getLoadFileWork();
boolean isCStats = pCtx.getQueryProperties().isAnalyzeRewrite();
int outerQueryLimit = pCtx.getQueryProperties().getOuterQueryLimit();
......
LoadFileDesc loadFileDesc = loadFileWork.get(0);
//拿到最终输出的列名和列类型
String cols = loadFileDesc.getColumns();
String colTypes = loadFileDesc.getColumnTypes();
String resFileFormat;
TableDesc resultTab = pCtx.getFetchTableDesc();
if (resultTab == null) {
//输出文件格式
resFileFormat = HiveConf.getVar(conf, HiveConf.ConfVars.HIVEQUERYRESULTFILEFORMAT);
if (SessionState.get().getIsUsingThriftJDBCBinarySerDe()
&& (resFileFormat.equalsIgnoreCase("SequenceFile"))) {
resultTab =
PlanUtils.getDefaultQueryOutputTableDesc(cols, colTypes, resFileFormat,
ThriftJDBCBinarySerDe.class);
conf.set(SerDeUtils.LIST_SINK_OUTPUT_FORMATTER, NoOpFetchFormatter.class.getName());
} else {
//inputFileFormatClass outputFileFormatClass properties(columns,escape.delim,serialization.format等信息)
resultTab =
PlanUtils.getDefaultQueryOutputTableDesc(cols, colTypes, resFileFormat,
LazySimpleSerDe.class);
}
........
//输入输出类,properties等信息转换为FetchWork类
//结果在临时目录,把临时目录的结果拉到终端显示或者表路径下面
FetchWork fetch = new FetchWork(loadFileDesc.getSourcePath(), resultTab, outerQueryLimit);
boolean isHiveServerQuery = SessionState.get().isHiveServerQuery();
fetch.setHiveServerQuery(isHiveServerQuery);
fetch.setSource(pCtx.getFetchSource());
fetch.setSink(pCtx.getFetchSink());
if (isHiveServerQuery &&
null != resultTab &&
resultTab.getSerdeClassName().equalsIgnoreCase(ThriftJDBCBinarySerDe.class.getName()) &&
HiveConf.getBoolVar(conf, HiveConf.ConfVars.HIVE_SERVER2_THRIFT_RESULTSET_SERIALIZE_IN_TASKS)) {
fetch.setIsUsingThriftJDBCBinarySerDe(true);
} else {
fetch.setIsUsingThriftJDBCBinarySerDe(false);
}
//FetchWork类转换为FetchTask类。 (FetchTask extends FetchWork)
pCtx.setFetchTask((FetchTask) TaskFactory.get(fetch));
// limit行 不能太大
int fetchLimit = HiveConf.getIntVar(conf, HiveConf.ConfVars.HIVELIMITOPTMAXFETCH);
if (globalLimitCtx.isEnable() && globalLimitCtx.getGlobalLimit() > fetchLimit) {
LOG.info("For FetchTask, LIMIT " + globalLimitCtx.getGlobalLimit() + " > " + fetchLimit
+ ". Doesn't qualify limit optimization.");
globalLimitCtx.disableOpt();
.......
//这步对mapreduce没有什么用
// rootTask 里面有一个
work对象 (work对象有一个mapWork reduceWork 对象)
childTasks (包含子job) 下面的路径解答了我一个问题,两个mapreduce,下一个mapreduce是怎么知道上一个mapreduce的结果路径的
Path file:/tmp/root/99d5f502-9261-4fab-abc2-24cc01945ecc/hive_2022-08-09_21-50-58_649_7306579306496217629-1/-mr-10004
generateTaskTree(rootTasks, pCtx, mvTask, inputs, outputs);
for (Task<? extends Serializable> rootTask : rootTasks) {
GenMapRedUtils.setKeyAndValueDescForTaskTree(rootTask);
}
//设置input参数
for (Task<? extends Serializable> rootTask : rootTasks) {
setInputFormat(rootTask);
}
//物理执行计划的优化比较简单:我的SQL只是执行了下面五个
//CommonJoinResolver 确定哪些表可以mapjoin
//MapJoinResolver 本地计算
//NullScanOptimizer 1. limit 0删除输入路径 2.FIL在编译时等于0 这个翻译不太懂。应该是路径不存在吧
//CrossProductHandler 优化tez和mr join
//Vectorizer udf hive
optimizeTaskPlan(rootTasks, pCtx, ctx);
decideExecMode(rootTasks, ctx, globalLimitCtx);
......
//limit处理 设置一个limit处理函数
if (globalLimitCtx.isEnable() && pCtx.getFetchTask() != null) {
LOG.info("set least row check for FetchTask: " + globalLimitCtx.getGlobalLimit());
pCtx.getFetchTask().getWork().setLeastNumRows(globalLimitCtx.getGlobalLimit());
}
//limit处理 设置一个limit处理函数
if (globalLimitCtx.isEnable() && globalLimitCtx.getLastReduceLimitDesc() != null) {
LOG.info("set least row check for LimitDesc: " + globalLimitCtx.getGlobalLimit());
globalLimitCtx.getLastReduceLimitDesc().setLeastRows(globalLimitCtx.getGlobalLimit());
}
Interner<TableDesc> interner = Interners.newStrongInterner();
for (Task<? extends Serializable> rootTask : rootTasks) {
GenMapRedUtils.internTableDesc(rootTask, interner);
GenMapRedUtils.deriveFinalExplainAttributes(rootTask, pCtx.getConf());
}
}
OperatorTree==>>物理执行计划 总结
1.OperatorTree其实已经包含了执行sql语句必须的所有条件的:
输入输出路径:table的元数据可以拿到最原始的输入路径
第一个reduce的输出路径可以从***_7306579306496217629-1/-mr-10004
这个路径同样的可以作为第二个输入路径
输入输出类型:
RS 本身就有需要获取的列的别名,物理执行计划里面有把数据类型下推
mapreduce切分:
直接根据RS进行mapreduce个数的切分
2.OperatorTree转换为物理执行计划
物理执行计划后需要通过设置input参数和keyvalue
3.物理执行计划优化
sql:select a.user_id,b.id from ( select id,user_id from data group by id,user_id)a inner join( select id,user_id from data group by id,user_id)b on a.id = b.user_id ;
这个SQL执行的优化比较简单:几乎全是join的优化。
//CommonJoinResolver 确定哪些表可以mapjoin
//MapJoinResolver 本地计算
//NullScanOptimizer 1. limit 0删除输入路径 2.FIL在编译时等于0 这个翻译不太懂。应该是路径不存在吧
//CrossProductHandler 优化tez和mr join
//Vectorizer udf hive
搬砖多年终不得要领,遂载源码看之望得真经。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?