快捷搜索:  汽车  科技

calcite 入门(Calcite的基本概念)

calcite 入门(Calcite的基本概念)代表表的数据定义,对应表的数据列名和类型。public interface Table { RelDataType getRowType(RelDataTypeFactory typeFactory); Statistic getStatistic(); Schema.TableType getJdbcTableType(); boolean isRolledUp(String column); boolean rolledUpColumnValidInsideAgg(String column SqlCall call SqlNode parent CalciteConnectionConfig config); }RelDataType3.1 catalogSchemapublic interface Schema { Table g

背景

Apache Calcite是一个面向Hadoop新的查询引擎,它提供了标准的SQL语言,多种查询和优化。calcite之前叫做optiq,是作为Hive的SQL引擎,为hive提供成本规模的优化功能即CBO (Cost Based Optimizatio) 2014年5月作为独立孵化项目开源。关于Calcite 的架构有三个特点:flexible embeddable and extensible,就是灵活性、组件可插拔、可扩展,它的 SQL Parser 层、Optimizer 层等都可以单独使用,这也是 Calcite 受总多开源框架欢迎的原因之一。

calcite 入门(Calcite的基本概念)(1)

Calcite的架构

关于calcite的架构,相对于传统数据库,他将数据存储和数据处理算法和元信息存储忽略了,这样使得calcite更加兼容,适合统一查询服务。

calcite 入门(Calcite的基本概念)(2)

由上图可以看到,calcite最核心的就是optimizer优化器,一个optimizer engine包含三个组成部分:

  • rules:也就是匹配规则,calcite目前内置上百种rule来优化relations expression 也支持自定义的rules
  • Metadata providers:主要向优化器提供信息 有助于指导优化器向着目标(减少整体cost)进行优化,信息可以包括行数,table哪一列是唯一列,也包括计算relNode树,执行subexpression cost的函数。
  • Planner engines:触发rules来达到指定目标,比如cost-base expression optimizer(CBO)的目标是减少cost(减少数据行数,CPU cost IO cost等)。

对SQL执行完整流程,分为4部分

calcite 入门(Calcite的基本概念)(3)

  • Parser: 解析,calcite通过javaCC将SQL解析成未经校验的AST(Abstract Syntax Tree 即抽象语法树)。
  • Validate: 验证,主要负责校验上一步parser是否合法,如验证SQL的schema信息,字段,函数等是否存在,SQL是否合法,此步骤完成后生成RealNode。
  • Optimize:优化,也是整个核心所在,将逻辑计划RelNode转化为物理计划,主要涉及SQL优化:基于规则优化(RBO),基于代价优化(CBO) optimizer按道理是可以选的。此步骤得到物理计划
  • Execute:执行阶段,将物理计划转成程序,变成自己平台的可执行代码。
Calcite的相关概念

3.1 catalog

  • schema: 主要定义schema与表的结合,有点像database,但是sechma不一定是强制的,比如同名表T1就需要加上A.T1 B.T1.
  • table:对应数据库的表
  • RelDataType:代表表的数据定义,对应表的数据列名和类型。

Schema

public interface Schema { Table getTable(String name); Set<String> getTableNames(); RelProtoDataType getType(String name); Set<String> getTypeNames(); Collection<Function> getFunctions(String name); Set<String> getFunctionNames(); Schema getSubSchema(String name); Set<String> getSubSchemaNames(); Expression getExpression(SchemaPlus parentSchema String name); boolean isMutable(); Schema snapshot(SchemaVersion version); /** Table type. */ enum TableType {} }

Table

public interface Table { RelDataType getRowType(RelDataTypeFactory typeFactory); Statistic getStatistic(); Schema.TableType getJdbcTableType(); boolean isRolledUp(String column); boolean rolledUpColumnValidInsideAgg(String column SqlCall call SqlNode parent CalciteConnectionConfig config); }

RelDataType

代表表的数据定义,对应表的数据列名和类型。

3.2 SQL Parse(SQL -> SQL Node)

Calcite使用javaCC作为SQL解析,javaCC通过Calcite中定义的Parse.jj文件,生成一系列的java代码,生成的代码会把SQL转换成AST的数据结构(SQL Node类型)

与 Javacc 相似的工具还有 ANTLR,JavaCC 中的 jj 文件也跟 ANTLR 中的 G4文件类似,Apache Spark 中使用这个工具做类似的事情。

javaCC要实现一个SQL parser,他的功能有两点,这里都需要在 jj 文件中定义

  1. 设计词法和语义,定义SQL中的具体元素。
  2. 实现词法分析器(Lexer)和语法分析器(Parse) 完成对SQL的解析,完成相应的转换。

//org.apache.calcite.prepare.CalcitePrepareImpl //... //解析sql->SqlNode SqlParser parser = createParser(query.sql parserConfig); SqlNode sqlNode; try { sqlNode = parser.parseStmt(); statementType = getStatementType(sqlNode.getKind()); } catch (SqlParseException e) { throw new RuntimeException( "parse failed: " e.getMessage() e); } //...

经过上述代码sql经过parser.parseStmt解析之后,会生成一个SqlNode Debug后的对象如图所示

calcite 入门(Calcite的基本概念)(4)

3.3 验证和转换 (SQL Node -> RelNode)

经过上面的解析,会将SQL翻译成SQL 的抽象语法树 AST SQL Node就是抽象语法树的节点Node 而Rel代表关系表达式(Relation Expression) 所以从sql node -> rel node 的过程,是一个转换,一个校验的过程。

//org.apache.calcite.prepare. Prepare //function prepareSql RelRoot root = sqlToRelConverter.convertQuery(sqlQuery needsValidation true);

3.3.1 校验Validate

语法检查前我们要先知道元信息,这个检查会包括表名,字段名,字段类型,函数名,进行语法检查的实现如下:

//org.apache.calcite.sql.validate.SqlValidatorImpl public SqlNode validate(SqlNode topNode) { SqlValidatorScope scope = new EmptyScope(this); scope = new CatalogScope(scope ImmutableList.of("CATALOG")); final SqlNode topNode2 = validateScopedExpression(topNode scope); final RelDataType type = getValidatedNodeType(topNode2); Util.discard(type); return topNode2; }

在对SQL node的校验过程中,会对AST的语法进行补充,例如:

//校验前 select province avg(cast(age as double)) from t_user group by province //校验后 SELECT `T_USER`.`PROVINCE` AVG(CAST(`T_USER`.`AGE` AS DOUBLE)) FROM `TEST_PRO`.`T_USER` AS `T_USER` GROUP BY `T_USER`.`PROVINCE`

3.3.2 SQL Node->Rel Node

在经过上面进行合法的补充了一定语义的AST树之后,而validate中已经准备好各种映射信息后,就开始具体的RelNode的转换了。

//org.apache.calcite.sql2rel.SqlToRelConverter protected RelRoot convertQueryRecursive(SqlNode query boolean top RelDataType targetRowType) { final SqlKind kind = query.getKind(); switch (kind) { case SELECT: return RelRoot.of(convertSelect((SqlSelect) query top) kind); case INSERT: return RelRoot.of(convertInsert((SqlInsert) query) kind); case DELETE: return RelRoot.of(convertDelete((SqlDelete) query) kind); case UPDATE: return RelRoot.of(convertUpdate((SqlUpdate) query) kind); case MERGE: return RelRoot.of(convertMerge((SqlMerge) query) kind); case UNION: case INTERSECT: case EXCEPT: return RelRoot.of(convertSetOp((SqlCall) query) kind); case WITH: return convertWith((SqlWith) query top); case VALUES: return RelRoot.of(convertValues((SqlCall) query targetRowType) kind); default: throw new AssertionError("not a query: " query); } }

测试SQL

select province avg(cast(age as double)) from t_user group by province

得到的语法树

Plan after converting SqlNode to RelNode LogicalAggregate(group=[{0}] EXPR$1=[AVG($1)]) LogicalProject(PROVINCE=[$3] $f1=[CAST($2):DOUBLE]) LogicalTableScan(table=[[TEST_PRO T_USER]])

3.4 优化 Optimizer

在calcite中有两种优化器

  • HepPlanner(RBO):它是一个启发式的优化器,按照规则进行匹配,知道达到次数限制,或者遍历一遍,不在出现rule match为止。
  • VolcanoPlanner(RBO CBO):他会一直迭代rule 知道找到最小的cost的plan

这里看下calcite 优化器的实现,RelOptPlanner是Calcite的基类,其子类实现如下图所示:

calcite 入门(Calcite的基本概念)(5)

3.4.1 基于规则的优化(RBO)

基于规则的优化器(Rule-Based Optimizer,RBO):根据优化规则对关系表达式进行转换,这里说的是,一个表达式A经过优化 得到另一个表达式B,得到B的同时A被裁掉,经过一系列转换得到最终的结果。

RBO有着一套严格顺序的优化规则,同样一条SQL,无论读取表中数据是什么样的,最后生成的执行计划都是相同的,所以SQL的性能可能和SQL本身的书写有很大的关系。

3.4.2 基于代价的优化 (CBO)

基于代价的优化器(Cost-Based Optimizer,CBO):也是根据优化规则对表达式进行转换,这里不同的是,表达式A在转换表达式B后,会被保存下来,根据不同的规则生成多个不同的表达式,CBO会根据统计模型和代价模型,在多个表达式选择一个cost最小的计划作为整个执行计划。

由上可知,CBO有两个依赖,一个是统计模型,一个是代价模型,目前大多数的计算引擎都趋向CBO,但是CBO比较有难度,因为不能明确预知数据量大小,所以CBO主要用于离线的场景。

感谢您的阅读,如果喜欢本文欢迎关注和转发,本头条号将坚持持续分享IT技术知识。对于文章内容有其他想法或意见建议等,欢迎提出共同讨论共同进步。

原文地址:

https://lixiyan4633.gitee.io/2020/03/18/calcite教程01-calcite的基本概念/

猜您喜欢: