Apache Calcite中的基本概念

Apache Calcite中的基本概念

  • Calcite的Adapter
  • Calcite中的关系表达式
  • Calcite的优化规则
  • Calcite的Trait
  • Calcite的Calling Convention
  • Calcite内建算子实现
  • 总结

在学习Apache Calcite的一些基本概念之前,首先要弄懂关系表达式,并且要知道SQL与关系表达式的关系,因为Calcite的最核心的概念就是关系表达式。SQL与关系表达式的相关概念可以参考这篇文章SQL 形式化语言——关系代数。通过jdbc简单使用calcite可以参考这篇文章。

Calcite的Adapter

在Calcite的架构中,Adapter的概念使Calcite知道如何去访问后端不同的数据源。一个数据源的Adapter对应有一个model,一个schema,一个schema Factory。
model定义了数据源的物理属性,比如下面的JDBC Adapter的model:

{
    "defaultSchema": "db1",
    "schemas": [
        {
            "factory": "org.apache.calcite.adapter.jdbc.JdbcSchema$Factory",
            "name": "db1",
            "operand": {
                "jdbcDriver": "com.mysql.cj.jdbc.Driver",
                "jdbcPassword": "changeme",
                "jdbcUrl": "jdbc:mysql://localhost:3306/test",
                "jdbcUser": "root"
            },
            "type": "custom"
        }
    ],
    "version": "1.0"
}

schema定义了数据的格式和层次。schema factory可以解析model来创建schema。
Apache Calcite中的基本概念_第1张图片

Calcite中的关系表达式

Apache Calcite中的基本概念_第2张图片
在calcite中,当一个sql字符串被解析为SqlNode结构的AST树后,并不能直接被Calcite的优化器优化。还需要通过SqlToRelConverter将SqlNode结构转换成为RelNode结构组成的树。
如上图所示,Calcite所有的关系表达式算子都需要实现RelNode接口。其中Calcite的核心算子有:TableScan, TableModify, Values, Project, Filter, Aggregate, Join, Sort, Union, Intersect, Minus, Window 与Match。通过这些类的名字比较容易理解它们与SQL的对应关系,比如TableModify对应SQL中的INSERT、UPDATE、CREATE操作,Minus对应SQL中的EXCEPT操作,Window对应流处理的window概念等。
Calcite中的这些核心算子都有对应的纯逻辑子类LogicalXXX,SqlToRelConverter将SqlNode转换出来的RelNode树中,每个节点都是这些LogicalXXX子类组成的。它们并不能生成可执行的执行计划,只是用于表示SQL对应的关系表达式结构。与Calcite对接的后端执行引擎的Adapter都会实现这些算子的可执行的实现。比如Cassandra adpter会有CassandraProject
在Calcite中逻辑算子转换成后端引擎实现的对应算子是在Calcite Planner(优化器)对RelNode树执行优化时候根据优化Rule将逻辑算子替换成对应的实际算子。

Calcite的优化规则

Apache Calcite中的基本概念_第3张图片
如上图,Calcite的优化器规则可以将一个关系表达式转换成等价的关系表达式,而这些优化器规则的基类都是RelOptRule。比如上图的EnumerableSortRule可以将LogicalSort逻辑算子转换成Calcite内建的可执行的算子EnumerableSort

EnumerableSortRule() {
    super(Sort.class, Convention.NONE, EnumerableConvention.INSTANCE,
        "EnumerableSortRule");
  }

EnumerableSortRule的构造函数如上,Sort.class的算子在优化器运行时匹配这条规则,然后优化器调用下面EnumerableSortRule的convert函数将原来的Sort算子转换为EnumerableSort算子。比如,LogicalSort在优化器优化时会匹配到这条规则,EnumerableSort就会替代LogicalSort

  public RelNode convert(RelNode rel) {
    final Sort sort = (Sort) rel;
    if (sort.offset != null || sort.fetch != null) {
      return null;
    }
    final RelNode input = sort.getInput();
    return EnumerableSort.create(
        convert(
            input,
            input.getTraitSet().replace(EnumerableConvention.INSTANCE)),
        sort.getCollation(),
        null,
        null);
  }

除了上面的转换规则以外,Calcite已经实现了许多规则可以注册到优化器中,在优化时调用,比如filter、project等算子下推的优化规则等。目前Calcite已经实现了两种优化器引擎:基于代价优化的class VolcanoPlanner和基于经验的优化class HepPlanner。

Calcite的Trait

在Calcite中没有使用不同的对象代表逻辑和物理算子,但是使用trait来表示一个算子的物理属性。
Apache Calcite中的基本概念_第4张图片
Calcite中使用接口RelTrait来代表一个关系表达式节点的物理属性,使用RelTraitDef来表示Reltrait的class。RelTrait与RelTraitDef的关系就像java中对象与Class的关系一样,每个对象都有Class。对于物理的关系表达式算子会有一些物理属性,这些物理属性都会用RelTrait来表示。比如每个算子都有Calling Convention这一Reltrait。比如上图中Sort算子还会有一个物理属性RelCollation,因为Sort算子会对表的一些字段进行排序,RelCollation这一物理属性就会记录这个Sort算子要排序的字段索引、排序方向,null值怎么排序等信息。

Calcite的Calling Convention

Calling Convention在Calcite中使用接口Convention表示,Convention接口是RelTrait的直接口,所以是一个算子的属性。Calling Convention可以理解为一个特定数据引擎协议,拥有相同Convention的算子可以认为都是统一个数据引擎的算子可以相互连接起来。比如JDBC的算子JDBCXXX都有JdbcConvention,Calcite的内建Enumerable算子EnumerableXXX都有EnumerableConvention
Apache Calcite中的基本概念_第5张图片
上图中,Jdbc算子可以通过JdbcConvention获得对应数据库的SqlDialect和JdbcSchema等数据,这样可以生成对应数据库的sql,获得数据库的连接池与数据库交互实现算子的逻辑。
如果数据要从一个Calling Convention的算子到另一个Calling Convention算子的时候,比如这篇使用Calcite进行跨库join文章描述的场景。需要接口Converter的子类作为两种算子之间的桥梁将两种算子连接起来。
Apache Calcite中的基本概念_第6张图片
比如上面的执行计划,要将Enumerable的算子与Jdbc的算子连接起来,中间就要使用JdbcToEnumerableConverter作为桥梁。

Calcite内建算子实现

虽然Calcite的重点没有放在数据处理这一块中,但是如果一个Adapter没有实现所有的核心关系表达式算子,Calcite也能生成完整的执行计划,完成整个数据处理过程。这是因为Calcite内建了EnumerableConvention作为Calling Convention的Enumerable算子,实现了所有核心的关系表达式算子,比如EnumerableFilter,EnumerableSrt,EnumerableHashJoin等。
但是需要注意的是使用Enumerable算子效率不高,而且都是在内存中处理的。比如EnumerableHashJoin算子join两个表都是在内存中处理表的所有数据,数据量一大就会造成内存溢出。所以一个数据引擎的Adapter实现,如果数据引擎能提供对应算子的处理能力就应该实现这个算子,将工作推到后端的数据引擎中处理,避免回退到使用Enumerable算子。

总结

以上按照自己的理解对Calcite的一些基本概念做了简单的解释。更多详细的信息可以访问官方文档和calcite论文。

你可能感兴趣的:(apache,calcite)