Calcite原理和代码讲解(一)

1.Calcite介绍


(1)简介
  • Apache Calcite 是面向 Hadoop 新的查询引擎,它提供了标准的 SQL 语言、多种查询优化和连接各种数据源的能力。
  • Calcite 的目标是“ one size fits all (一种方案适应所有需求场景)”, 希望能为不同计算平台和数据源提供统一的查询引擎,并以类似传统数据库的访问方式(SQL 和高级查询优化)来访问Hadoop 上的数据
  • Calcite 之前的名称叫做 optiq ,optiq 起初在 Hive 项目中,为 Hive 提供基于成本模型的优化,即 CBO(Cost Based Optimizatio)。2014 年 5 月 optiq 独立出来,成为 Apache 社区的孵化项目,2014 年 9 月正式更名为 Calcite。
(2)技术特性
  • 支持标准 SQL 语言;
  • 独立于编程语言和数据源, 可以支持不同的前端和后端(后面再补充解释)
  • 支持 关系代数、可定制的逻辑规划规则和基于成本模型优化的查询引擎
  • 支持物化视图( materialized view)的管理(创建、丢弃、持久化和自动识别)
  • 个人理解:flexible, embeddable, and extensible(灵活性、组件可插拔、可扩展)。它的 SQL Parser 层、Optimizer 层等都可以单独使用。

2.关系代数


  • 关系代数是关系型数据库操作的理论基础,关系代数支持 并、差、笛卡尔积、投影和选择等基本运算。
  • 关系代数也是 Calcite 的核心, 任何一个查询都可以表示成由关系运算符组成的树在 Calcite 中,它会先将 SQL 转换成关系表达式(relational expression),然后通过规则匹配(rules match)进行相应的优化,优化会有一个成本(cost)模型为参考
Calcite原理和代码讲解(一)_第1张图片    Calcite原理和代码讲解(一)_第2张图片

3.Calcite架构


  • calcite架构与传统数据库管理系统有一些相似之处,相比而言, 它将数据存储、数据处理算法和元数据存储这些部分忽略掉了
  • 好处是:对于涉及多种数据源和多种计算引擎的应用而言,Calcite 因为可以兼容多种存储和计算引擎,使得 Calcite 可以提供统一查询服务
  • Calcite 架构中,最核心地方就是 Optimizer,也就是优化器,一个 Optimization Engine 包含三个组成部分
    • Rules:也就是 匹配规则,Calcite 内置上百种 Rules 来优化 relational expression,当然也支持自定义 rules;
    • metadata providers:主要是向优化器提供信息,这些信息会有助于指导优化器向着目标(减少整体 cost)进行优化, 信息可以包括行数、table 哪一列是唯一列等,也包括计算 RelNode 树中执行 subexpression cost 的函数。
    • planner engines:它的主要目标是进行触发 rules 来达到指定目标,比如像 cost-based optimizer(CBO)的目标是减少cost (Cost 包括处理的数据行数、CPU cost、IO cost 等)
Calcite原理和代码讲解(一)_第3张图片

4.Calcite核心概念


  • RelOptRule:根据传递给它的 RelOptRuleOperand 来对目标 RelNode 树进行 规则匹配,匹配成功后,会再次调用 matches() 方法进行进一步检查。如果 mathes结果为真,则调用 onMatch() 进行转换。
  • ConverterRule:它是 RelOptRule 的子类,专门用来做 数据源之间的转换(Calling convention),ConverterRule 一般会调用对应的 Converter 来完成工作,比如说:JdbcToSparkConverterRule 调用 JdbcToSparkConverter 来完成对 JDBC Table 到 Spark RDD 的转换。
  • RelNode:relational expression,which contains input RelNode。代表了对数据的一个处理操作,常见的操作有 Sort、Join、Project、Filter、Scan 等。它蕴含的是对整个 Relation 的操作,而不是对具体数据的处理逻辑。
  • Converter: 用来把一种 RelTrait 转换为另一种 RelTrait 的 RelNode。如 JdbcToSparkConverter 可以把 JDBC 里的 table 转换为 Spark RDD。如果需要在一个 RelNode 中处理来源于异构系统的逻辑表,Calcite 要求先用 Converter 把异构系统的逻辑表转换为同一种 Convention。
  • RexNode 行表达式(标量表达式),蕴含的是对一行数据的处理逻辑每个行表达式都有数据的类型。这是因为在 Valdiation 的过程中,编译器会推导出表达式的结果类型。常见的行表达式包括字面量 RexLiteral, 变量 RexVariable, 函数或操作符调用 RexCall 等。 RexNode 通过 RexBuilder 进行构建。
  • RelTrait 用来定义逻辑表的物理相关属性(physical property),三种主要的 trait 类型是:Convention、RelCollation、RelDistribution;
  • Convention继承自 RelTrait,类型很少,代表一个单一的数据源,一个 relational expression 必须在同一个 convention 中;
  • RelTraitDef主要有三种: ConventionTraitDef:用来代表数据源。 RelCollationTraitDef:用来定义参与排序的字段。 RelDistributionTraitDef:用来定义数据在物理存储上的分布方式(比如:single、hash、range、random 等);
  • RelOptCluster palnner 运行时的环境,保存上下文信息
  • RelOptPlanner也就是 优化器,Calcite 支持 RBO(Rule-Based Optimizer) 和 CBO(Cost-Based Optimizer)。Calcite 的 RBO (HepPlanner)称为 启发式优化器(heuristic implementation ), 它简单地按 AST 树结构匹配所有已知规则,直到没有规则能够匹配为止;Calcite 的 CBO 称为 火山式优化器(VolcanoPlanner)成本优化器也会匹配并应用规则,当整棵树的成本降低趋于稳定后,优化完成,成本优化器依赖于比较准确的成本估算。RelOptCost 和 Statistic 与成本估算相关;
  • RelOptCost: 优化器成本模型会依赖。

5.Calcite处理SQL流程


    sql执行流程(目前看来基本上所有SQL编译器 都是相似流程,只是细节不同
  • 解析 SQL: 把 SQL 转换成为 AST (抽象语法树,在 Calcite 中用 SqlNode 来表示;
  • 语法检查: 根据数据库的元数据信息进行语法验证,验证之后还是用 SqlNode 表示 AST 语法树 (标准化修改:比如只写join会变成inner join)
  • 语义分析:根据 SqlNode 及元信息构建 RelNode 树,也就是 最初版本的逻辑计划(Logical Plan)
  • 逻辑计划优化:优化器的核心, 根据前面生成的逻辑计划按照相应的规则(Rule)进行优化(逻辑计划调整)
  • 物理执行:生成物理计划,物理执行计划执行 (具体执行计划就要看存储引擎如何设计,比较Mysql有Innodb,Hive默认使用HDFS存储)
(1)Step1: SQL 解析阶段(SQL–>SqlNode)
  • Calcite 进行 Sql 解析的代码如下:
    • SqlParser parser = SqlParser.create(sql, SqlParser.Config.DEFAULT);
    • SqlNode sqlNode = parser.parseStmt();
  • Calcite 使用 JavaCC( Java Compiler Compiler ) 做 SQL 解析,JavaCC 根据 Calcite 中定义的 Parser.jj 文件,生成一系列的 java 代码,生成的 Java 代码会把 SQL 转换成 AST 的数据结构( 这里是 SqlNode 类型)。
  • 与 Javacc 相似的工具还有 ANTLR,JavaCC 中的 jj 文件也跟 ANTLR 中的 G4文件类似,Apache Spark(Inceptor)中使用这个工具做类似的事情。
  • Javacc实现了一个 SQL Parser,它的功能有以下两个( 这里都是需要在 jj 文件中定义的):
    • 设计 词法和语义,定义 SQL 中具体的元素;
    • 实现 词法分析器(Lexer)和语法分析器(Parser),完成对 SQL 的解析,完成相应的转换。
  【SQL案例】
        
    解析sqlNode结果:
Calcite原理和代码讲解(一)_第4张图片
(2)Step2: SQL 验证阶段(SqlNode–>SqlNode)
  • 经过上一步,会生成一个 SqlNode 对象,它是一个未经验证的抽象语法树,下面就进入了一个语法检查阶段, 语法检查前需要知道元数据信息,这个检查会包括 表名、字段名、函数名、数据类型的检查
     Calcite原理和代码讲解(一)_第5张图片
  • Calcite 本身是不管理和存储元数据的,在检查之前, 需要先把元信息注册到 Calcite 中,一般的操作方法是实现 SchemaFactory由它去创建相应的 Schema,在 Schema 中可以注册相应的元数据信息
     Calcite原理和代码讲解(一)_第6张图片
  • SqlValidatorImpl 检查过程
    1. rewrite expression, 将其标准化,便于后面的逻辑计划优化
    2. 注册这个 relational expression 的 scopes 和 namespaces 这两个对象代表了其元信息
    3. 进行相应的验证,这里会依赖第二步注册的 scopes 和 namespaces 信息。
     Calcite原理和代码讲解(一)_第7张图片
(3)Step3: SQL 语义分析阶段( SqlNode –>RelNode/RexNode)
  • 主要步骤:
  1. 初始化 RexBuilder;
  2. 初始化 RelOptPlanner;
  3. 初始化 RelOptCluster;
  4. 初始化 SqlToRelConverter;
  5. 进行转换;
     Calcite原理和代码讲解(一)_第8张图片
  • 转换部分
    • SqlToRelConverter 中的 convertQuery() 将 SqlNode 转换为 RelRoot
    • 真正实现部分:convertQueryRecursive
    

Calcite原理和代码讲解(一)_第9张图片

     Calcite原理和代码讲解(一)_第10张图片
(4)Step4: 优化阶段( RelNode –>RelNode)
  • 提供了两种 planner:HepPlanner 和 VolcanoPlanner
  • HepPlanner
    • HepPlanner is a heuristic optimizer similar to Spark’s optimizer: 与 spark 的优化器相似,HepPlanner 是一个 heuristic 优化器
    • Applies all matching rules until none can be applied: 将会匹配所有的 rules 直到某个 rule 被满足
    • Heuristic optimization is faster than cost- based optimization:它比 CBO 更快;
    • Risk of infinite recursion if rules make opposing changes to the plan: 如果没有每次都不匹配规则,可能会有无限递归风险
  • VolcanoPlanner
    • VolcanoPlanner is a cost-based optimizer:VolcanoPlanner是一个CBO优化器;
    • Applies matching rules iteratively, selecting the plan with the cheapest cost on each iteration: 迭代地应用 rules,直到找到cost最小的plan
    • Costs are provided by relational expressions;
    • Not all possible plans can be computed: 不会计算所有可能的计划(如何判断?有时间再补充...)
    • Stops optimization when the cost does not significantly improve through a determinable number of iterations:根据已知的情况, 如果下面的迭代不能带来提升时,这些计划将会停止优化(类似与机器学习中损失函数,只不过early_stop为1个epoch
  • 简单的优化 (过滤条件下压,也称为谓词下推)
    • 关于filter 操作下压,在 Calcite 中已经有相应的 Rule 实现,就是 FilterJoinRule.FilterIntoJoinRule.FILTER_ON_JOIN
    • 这里使用 HepPlanner 作为示例的 planer,并注册 FilterIntoJoinRule 规则进行相应的优化这里可以看到where u.age>10 and j.id>0被下推到扫表操作上面,也就是说SQL引擎扫描一遍表时如果发现表中某行满足过滤条件则拿取出来,否则跳过

      Calcite原理和代码讲解(一)_第11张图片

      参考资料


      https://www.cnblogs.com/wcgstudy/p/11795886.html
      https://www.infoq.cn/article/new-big-data-hadoop-query-engine-apache-calcite
      https://www.jianshu.com/p/2dfbd71b7f0f
      https://www.slideshare.net/JordanHalterman/introduction-to-apache-calcite
       
        
         Calcite原理和代码讲解(一)_第12张图片

你可能感兴趣的:(Hadoop,数据库,hadoop,calcite,sql,spark,数据库)