论文:https://arxiv.org/pdf/1802.10233.pdf
官网:Apache Calcite • Dynamic data management framework
一、是什么?
1、动态数据管理框架

Calcite包含组成典型数据库管理系统的许多部分,但省略了一些关键功能:数据存储、处理数据的算法和存储元数据的存储库。
Calcite 有意避开存储和处理数据的业务。正如我们将看到的,这使其成为在应用程序与一个或多个数据存储位置和数据处理引擎之间进行调解的绝佳选择。它也是构建数据库的完美基础:只需添加数据。
-
- Calcite是一个动态数据管理框架,用于构建数据库系统的语法解析模块
- Calcite不包含数据存储、数据处理等功能
- 可以编写Adaptor(适配器)实现功能扩展,以支持不同的数据处理平台
2、架构

- JDBC Server:主要供外部Client调用,获取输入的SQL语句
- SQL Parser and Validator:SQL的解析和校验
- Operator Expressions:处理关系表达式
- Query Optimizer:专注于查询优化
- Expression Builder:与框架对接,支持SQL校验和解析
- Metadata Providers:支持外部自定义元数据
- Pluggable Rules:定义优化规则
二、干什么?
2.1、Calcite解析SQL过程
1)解析(Parser):通过JavaCC将SQL解析成未经校验的AST(SqlNode,抽象语法树)。
2)验证(Validate):校验AST(SqlNode)的合法性。
3)转换(Converter):将SqlNode树转换为关系代数(RelNode)。
4)优化(Optimize):优化RelNode树,转化成物理执行计划。包括RBO(基于规则优化)和(CBO)基于成本优化。
5) 执行(Execute):将物理执行计划转换成可在特定平台执行的程序。
三、如何干?
3.1、解析案例
3.1.1、SQL语句
SELECT u.id, name, age, sum(price) FROM users AS u join orders AS o ON u.id = o.user_id WHERE age >= 20 AND age <= 30 GROUP BY u.id, name, age ORDER BY u.id
3.1.2、SQL解析
String sql = "SELECT u.id, name, age, sum(price) " + "FROM users AS u join orders AS o ON u.id = o.user_id " + "WHERE age >= 20 AND age <= 30 " + "GROUP BY u.id, name, age " + "ORDER BY u.id"; // 创建SqlParser, 用于解析SQL字符串 SqlParser parser = SqlParser.create(sql, SqlParser.Config.DEFAULT); // 解析SQL字符串, 生成SqlNode树 SqlNode sqlNode = parser.parseStmt();
SqlNode为根结点,SqlNode是SqlOrderBy类型, 它的query字段是一个SqlSelect类型, 即代表原始的SQL语句去掉ORDER BY部分. 图中红色矩形框内的其实都是SqlNode类型.

3.1.3、SQL验证
Calcite中的SQL验证阶段一方面会借助元数据信息对SQL语句进行语义检查, (如SQL中指定的表是否存在于数据库中, 字段是否存在于表中等);另一方面会对SqlNode树进行改写, 以转化为统一的格式, 方便下一步处理。

- SQL验证后的输出结果仍是SqlNode树, 不过其内部结构发生了改变.。一个明显的变化是验证后的SqlOrderBy节点被改写为了SqlSelect节点, 并在其orderBy变量中记录了排序字段。
- Calcite在验证时会为每个字段加上表名限定, 为每个表加上Schema限定。
-- 验证前的SqlNode树打印结果 SELECT `u`.`id`, `name`, `age`, SUM(`price`) FROM `users` AS `u` INNER JOIN `orders` AS `o` ON `u`.`id` = `o`.`user_id` WHERE `age` >= 20 AND `age` <= 30 GROUP BY `u`.`id`, `name`, `age` ORDER BY `u`.`id` -- 验证后的SqlNode树打印结果 SELECT `u`.`id`, `u`.`name`, `u`.`age`, SUM(`o`.`price`) FROM `s`.`users` AS `u` INNER JOIN `s`.`orders` AS `o` ON `u`.`id` = `o`.`user_id` WHERE `u`.`age` >= 20 AND `u`.`age` <= 30 GROUP BY `u`.`id`, `u`.`name`, `u`.`age` ORDER BY `u`.`id`
3.1.4、转化为关系代数(RelNode)
RelNode树可以理解为一个逻辑执行计划, 上述SQL对应的逻辑执行计划如下, 其中每一行都表示一个节点, 是RelNode的实现类, 缩进表示父子关系。
LogicalSort(sort0=[$0], dir0=[ASC]) LogicalAggregate(group=[{0, 1, 2}], EXPR$3=[SUM($3)]) LogicalProject(id=[$0], name=[$1], age=[$2], price=[$6]) LogicalFilter(condition=[AND(>=($2, 20), <=($2, 30))]) LogicalJoin(condition=[=($0, $4)], joinType=[inner]) LogicalTableScan(table=[[s, users]]) LogicalTableScan(table=[[s, orders]])
3.1.5、查询优化
查询优化是Calcite的核心模块, 它主要有三部分组成:
- Planner rules: 即优化规则, Calcite内置很多优化规则, 如谓词下推, 投影下推等,也可自定义的优化规则。
- Metadata providers: 元数据主要用于基于成本的优化(Cost-based Optimize, CBO)中, 包括表的行数, 表的大小, 给定列的值是否唯一等信息。
- Planner engines: Calcite提供了两种优化器实现, HepPlanner用于实现基于规则的优化(Rule-based Optimize, RBO), VolcanoPlanner用于实现基于成本的优化。

对比优化前后的计划, 值得注意的变化是对users表的过滤位置发生了变动, 从先Join后过滤变成了先过滤后Join, 如下图所示。
3.1.6、生成执行计划
将物理计划转化为执行计划,用于实现对数据的操作。
参考文献:
1、Apache Calcite整体架构及处理流程 - 知乎
2、Apache Calcite: A Foundational Framework for Optimized Query Processing Over Heterogeneous Data Sources https://arxiv.org/pdf/1802.10233.pdf
3、SQL执行计划的基本概念 SQL执行计划的基本概念 - 知乎
4、Apache Calcite 论文学习笔记 Apache Calcite 论文学习笔记 - 掘金
5、Calcite - SQL 解析框架 Calcite - SQL 解析框架 - 知乎
6、Calcite 功能简析及在 Flink 的应用 【Flink SQL引擎】:Calcite 功能简析及在 Flink 的应用 - 程序员大本营