• 基于calcite做傻瓜式的sql优化(二)


     上一篇:基于calcite做傻瓜式的sql优化(一)

    因为主要想借助hive的思路来实现对sql的优化,所以这一篇主要是梳理一条sql在hive内部大概是什么样的生命周期

    首先通过一张图看下,内部sql大概执行流:

     sql经过一系列的规则处理后,最后变成task tree,然后mapreduce通过task tree来执行job

    接下来通过源码,看下是如何处理的!

    另外我把编译好的hive(1.2.1版本)和hadoop(2.7.0版本)代码放在Git上,这样感兴趣的同学直接下载下来,就可以在本地debug跑

    Hive编译后的源码:https://github.com/niutaofan/apache-hive-1.2.1-src.git
    hadoop编译后的源码:链接:https://pan.baidu.com/s/1meF9MFHUAyY1Mk7mMOdqIg  密码:fwnh

    1、大体流程

    1)、Driver.compile 接收SQL , 然后通过:pd.parse(command)将SQL转换为ASTNode(这个过程包含了词法解析和语法解析)

      1.1、ParseDriver.parse接收到sql语句,然后通过:r = parser.statement();解析了词法和语法

      1.2、拿到解析后的HiveParser.statement_return,然后通过ASTNode tree = (ASTNode) r.getTree();获取到ASTNode

    2)、通过sem.analyze(tree, ctx);从AST Node到Phsical Optimize这几个阶段,都是在SemanticAnalyzer.analyzeInternal()方法中进行的(语义解析、生成逻辑执行计划、优化逻辑执行计划等)

      2.1、拿到ASTNode之后,通过SemanticAnalyzer.analyzeInternal()进行优化;

      2.2、代码会调度到CalcitePlanner.analyzeInternal (这个方法内部会做 一个流程的判断:if (runCBO) 是否执行CBO优化),当然不管执行RBO还是CBO,最后调用的都是:SemanticAnalyzer.analyzeInternal()

      2.3、在SemanticAnalyzer.analyzeInternal()方法中,首先基于ASTNode做了各种规则优化,根据需求包括了笼统的:RBO和CBO的优化,最终返回Operator

        在Hive中,使用Calcite来进行核心优化,它将AST Node转换成QB,又将QB转换成Calcite的RelNode,在Calcite优化完成后,又会将RelNode转换成Operator Tree,说起来很简单,但这又是一条很长的调用链。

        Calcite优化的主要类是CalcitePlanner,更加细节点,是在CalcitePlannerAction.apply()这个方法,CalcitePlannerAction是一个内部类,包括将QB转换成RelNode,优化具体操作都是在这个方法中进行的。

      

    2、一条sql的源码之路

    如果想debug的方式走读源码,那么需要如下几个步骤:

    第一步:启动本地的hadoop源码(NameNode和DataNode)

     第二步:启动hive的metastore服务

     第三步:启动(Debug方式)CliDriver类

    根据上文提示, sql在客户端执行后,会在Driver.compile 接收SQL , 然后通过:pd.parse(command)将SQL转换为ASTNode(这个过程包含了词法解析和语法解析)

    eg. 执行一段sql(sql的数据,提前放入hive了) , 看下hive是如何解析和优化的

    select 
      * 
    from 
      (
        select 
          Sname, 
          Sex, 
          Sage, 
          Sdept, 
          count(1) as num 
        from 
          student_ext 
        group by 
          Sname, 
          Sex, 
          Sage, 
          Sdept
      ) t1 
    where 
      Sage > 10;

    Driver.compile代码:

     上图比较重要的点:

    ParseDriver

    Hive使用的是antlr来做词法、语法的解析工作,最终生成一棵有语义的ast树
    而在Hive中调用antlr类的代码org.apache.hadoop.hive.ql.parse.ParseDriver类,通过ParseDriver.parse 可以返回HiveParser.statement_return 

    而这个HiveParser.statement_return通过强转,即可拿到ASTNode,如下图:

     ps:这块儿存在极大的性能问题,后续会提到并改进

    ######################################################思考############################################################################################

    如果需求是快速实现对用户输入的sql进行词法和语法解析,以便达到自定义或者sql优化的需求,那么可不可以利用上述内容进行重构???

    答案是肯定可以的,而且非常简单,只需要知道,hive在做sql的词法和语法解析,使用的是哪个包(org.apache.hadoop.hive.ql.parse)

    然后开启一个新的工程,导入hive-exec包即可

    第一步:maven导入依赖

     <dependency>
                <groupId>org.apache.hive</groupId>
                <artifactId>hive-exec-nt</artifactId>
                <version>1.2.1</version>
     </dependency>
     <dependency>
                <groupId>org.apache.hadoop</groupId>
                <artifactId>hadoop-common</artifactId>
                <version>2.7.1</version>
     </dependency>

    第二步:代码编写 

    import org.apache.hadoop.hive.ql.parse.ASTNode;
    import org.apache.hadoop.hive.ql.parse.ParseDriver;
    import org.apache.hadoop.hive.ql.parse.ParseException;
    /**
     * Created by niutao
     */
    public class Tests {
        public static void main(String[] args) {
            String sql = "SELECT `object_id`, `column1_id`, COUNT(DISTINCT `cookie`) AS `COOKIE`
    " +
                    "FROM `D_DSJ_INDEX_PDS`.`INDEX2_FLW_COOKIE_INTEREST_OBJECT_D_FACT`
    " +
                    "WHERE `dim_day` >= '2020-03-03' AND `dim_day` <= '2020-03-16' AND `series_id` = '692'
    " +
                    "GROUP BY `object_id`, `column1_id`
    " +
                    "ORDER BY COUNT(DISTINCT `cookie`) IS NULL DESC, COUNT(DISTINCT `cookie`) DESC
    " +
                    "LIMIT 200";
            //1、导入模仿hive,导入ParseDriver
            ParseDriver pd = new ParseDriver();
            //2、解析sql
            try {
                ASTNode ast = pd.parse(sql);
                //3、测试,打印解析树
                System.out.println(ast.dump());
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
    }

    打印 结果:

     通过以上方式,即可将sql解析出ASTNode

    ###################################################################################################################################################### 

    接着之前的源码,看下在生成ASTNode之后 , 是如何根据ASTNode来做优化的;

    请查看下一篇:基于calcite做傻瓜式的sql优化(三)

  • 相关阅读:
    JDBC 关闭数据库连接与自动提交【转】
    va注解应用实例
    Java IO流操作汇总: inputStream 和 outputStream【转】
    dom4j,json,pattern性能对比【原】
    JSP中setattribute与setParameter的区别
    setAttribute()和getAttibute(),getParameter()
    org.hibernate.MappingException: Unknown entity
    SQL保留关键字不能用作表名
    缺jstl.jar包导致的代码出现异常
    sessionFactory
  • 原文地址:https://www.cnblogs.com/niutao/p/14024383.html
Copyright © 2020-2023  润新知