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
posted @ 2022-08-09 22:39  Kotlin  阅读(241)  评论(0编辑  收藏  举报
Live2D