【大数据框架】Calcite 基础概念:从 SQL 到执行计划的思维路径 文章目录零核心概念链条一、从 SQL 语法到关系代数为什么需要数学抽象二、RelNode关系表达式的树形表示三、RelNode 与 RexNode关系级与行级的分离四、Convention不同执行引擎的统一抽象五、RelOptRule基于规则的优化机制六、优化器启发式与基于成本的两种策略七、Adapter连接不同数据源的桥梁八、从逻辑计划到物理计划逻辑计划关心做什么物理计划关心怎么做Calcite 的设计目标是one size fits all为不同计算存储引擎提供统一的 SQL 查询引擎。它的架构有三个核心特点flexible灵活性、embeddable可嵌入、extensible可扩展。核心问题Calcite 如何将 SQL 转换为可执行计划这个过程中需要哪些核心概念零核心概念链条理解 Calcite 的核心概念需要把握它们之间的完整链条SQL 是声明式的但优化需要数学基础所以需要关系代数。关系代数是数学概念需要数据结构表示所以有了 RelNode。RelNode 处理整个关系但还需要处理行级数据所以有了 RexNode。RelNode 是统一抽象但不同执行引擎有不同实现所以有了 Convention。有了 RelNode 树需要优化所以有了 RelOptRule。规则定义了如何转换但需要选择最优计划所以有了优化器。有了优化器但需要访问不同数据源所以有了 Adapter。RelNode 有逻辑和物理两种形态所以需要从逻辑计划转换为物理计划。这些概念共同支撑了 Calcite 的架构设计使得 Calcite 可以灵活地处理不同的数据源和执行引擎实现真正的联邦查询。每个概念都解决了前一个概念带来的新问题形成了一个完整的思维链条。一、从 SQL 语法到关系代数为什么需要数学抽象SQL 是声明式的语言描述了要什么而不是怎么做。但优化器需要知道如何优化这就需要将 SQL 转换为可以进行数学变换的形式。关系代数提供了这个基础。关系代数是关系型数据库操作的理论基础支持并、差、笛卡尔积、投影和选择等基本运算。更重要的是关系代数提供了等价变换规则两个关系表达式如果产生相同的结果就可以相互替换。通过将 SQL 转换为关系代数Calcite 实现了查询逻辑与优化逻辑的解耦优化器可以应用各种优化规则而不需要理解 SQL 的具体语法。二、RelNode关系表达式的树形表示关系代数是数学概念在代码中需要一种数据结构来表示这就是 RelNode。RelNode 是关系表达式的核心抽象代表了对数据的一个处理操作。它的名称通常是动词Sort、Join、Project、Filter、Scan。RelNode 会标识其 input RelNode 信息这样就构成了一棵 RelNode 树。RelNode 代表的是对整个 Relation关系的操作它蕴含的是做什么而不是怎么做。例如LogicalJoin 表示需要做连接操作但不指定如何连接是 hash join 还是 sort merge join。树形结构使得查询可以被递归地优化和执行每个节点只关注自己的操作通过组合不同的节点可以表达复杂的查询逻辑。三、RelNode 与 RexNode关系级与行级的分离在 RelNode 中我们还需要表达对一行数据的处理逻辑比如age 1这样的表达式。这就引出了 RexNode。RelNode 是关系表达式代表对整个关系的操作如 Sort、Join、Project、Filter。它处理的是整个表。RexNode 是行表达式标量表达式蕴含的是对一行数据的处理逻辑如age 1、name。它处理的是表中的一行数据。例如在SELECT name, age 1 FROM users WHERE age 18中age 1是 RexNode行表达式而整个 SELECT 查询是 RelNode关系表达式。这种分层设计将关系操作和标量计算分离使得优化器可以分别优化关系操作如 join 顺序和标量计算如表达式简化提高了优化的灵活性。四、Convention不同执行引擎的统一抽象有了 RelNode但不同的执行引擎Spark、Flink、JDBC有不同的实现方式。如何统一处理Convention调用约定解决了这个问题。Convention 就像是不同执行引擎的语言。Spark 有自己的 ConventionFlink 有自己的 ConventionJDBC 也有自己的 Convention。同数据引擎的 RelNode 可以直接相互连接而非同引擎的则需要通过 Converter 进行转换。如果要在同一个查询中处理来源于异构系统的逻辑表Calcite 要求先用 Converter 把异构系统的逻辑表转换为同一种 Convention。Convention 机制实现了执行引擎的抽象使得 Calcite 可以统一处理不同的数据源和执行引擎支持联邦查询。五、RelOptRule基于规则的优化机制有了 RelNode 树如何优化关系代数提供了等价变换规则但需要一种机制来应用这些规则。RelOptRule规则提供了这个机制。规则定义了如何将一个关系表达式转换为另一个等价的、更优的表达式。优化器会遍历 RelNode 树尝试匹配每个规则。如果规则匹配成功就会应用规则进行转换。ConverterRule 是规则的一种专门用来做数据源之间的转换。例如JdbcToSparkConverterRule 可以将 JDBC Table 转换为 Spark RDD。基于规则的优化系统具有高度的可扩展性每个优化规则都是独立的可以轻松添加新的优化规则规则之间相互独立便于维护。六、优化器启发式与基于成本的两种策略规则定义了如何转换但如何选择最优的执行计划这需要优化器。Calcite 支持两种优化器。HepPlanner 是启发式优化器它简单地按树结构匹配所有已知规则直到没有规则能够匹配为止。启发式优化比基于成本的优化更快适合规则简单明确的场景。VolcanoPlanner 是火山式优化器它会匹配并应用规则当整棵树的成本降低趋于稳定后优化完成。成本优化器依赖于比较准确的成本估算。提供两种优化器体现了 Calcite 的灵活性不同的场景需要不同的优化策略。如果优化规则简单明确可以使用 HepPlanner 快速优化。如果需要基于成本选择最优执行计划特别是在复杂查询场景下应该使用 VolcanoPlanner。七、Adapter连接不同数据源的桥梁有了优化器但如何访问不同的数据源Adapter 解决了这个问题。Adapter 就像是不同数据源的翻译器。它告诉 Calcite 如何访问数据源如何将数据源的表转换为 RelNode如何将逻辑计划转换为数据源可以执行的计划。一个数据源的 Adapter 对应有一个 model定义数据源的物理属性、一个 schema定义数据的格式和层次、一个 schema Factory解析 model 来创建 schema。Adapter 机制实现了数据源的抽象使得 Calcite 可以统一处理不同的数据源而不需要为每个数据源编写特定的代码。这种设计支持联邦查询可以在一个 SQL 查询中访问多个数据源。八、从逻辑计划到物理计划逻辑计划关心做什么物理计划关心怎么做RelNode 树有两种形态逻辑计划和物理计划。Calcite 的核心算子都有对应的纯逻辑子类LogicalXXX如 LogicalProject、LogicalFilter、LogicalJoin。它们并不能生成可执行的执行计划只是用于表示 SQL 对应的关系表达式结构。与 Calcite 对接的后端执行引擎的 Adapter 都会实现这些算子的可执行的实现如 CassandraProject。在优化器对 RelNode 树执行优化时会根据优化 Rule 将逻辑算子替换成对应的物理算子。这种设计将逻辑计划与物理计划分离逻辑计划LogicalXXX只关注做什么物理计划如 CassandraProject关注怎么做。优化器通过规则将逻辑计划转换为物理计划这种分离使得优化器可以独立于执行引擎进行优化。