ABSTRACT
calcite的特点:
- 模块化优化规则和可扩展查询优化器
- 支持各种查询语言的查询处理器
- 可扩展适配器架构
- 异构数据模型和存储
1.INTRODUCTION
面临问题:
- 多种异构数据源兴起(列存、流等),没有统一的优化和语言支持,跨数据源开发浪费精力。
- 经常需要将多个异构数据源集成到一起使用,需要有一个支持异构数据源的查询优化系统。
calcite,完整的查询处理系统,提供许多通用功能:查询、执行、优化、查询语言。但不包括数据存储和管理。calcite通过向多个系统公开一个通用的接口实现跨平台优化。
calcite的优势:
- 开源友好
- 多种数据模型
- 灵活的查询优化器
- 跨系统支持
- 可靠性
- 支持SQL及其扩展
2.RELATED WORK
略
3.ARCHITECTURE
calcite包含了组成典型数据库管理系统的部分,但是省略了一些关键组件:数据存储、数据处理算法、元数据仓库。正是因为没有这些组件,让calcite成为构建多数据引擎的应用程序的绝佳选择。
上图是calcite架构的主要组件。calcite的优化器使用关系运算符树(tree of relational operators)作为其内部表示。优化器引擎主要包括三部分:
- 规则(rules)
- 元数据提供者(metadata providers)
- 规划器引擎(planner engine)
通过parser和validator,calcite将sql转换为关系运算符树(tree of relational operators)。然后在optimizer中进行优化,最后执行阶段,calcite提供了一种通过适配器在外部存储引擎中定义表模式和视图的机制,使它可以运行在这些引擎之上。
提供sql查询和优化能力是calcite的典型应用场景,另外它还可以应用与如下场景:
- 已经了sql查询能力,但是没有或者有限的sql优化能力(比如hive或spark):可以将 sql转换为关系表达式 (relational expression),使用calcite进行优化,优化完成后再转换成sql去原来的引擎中执行。也就是calcite可以作为一个独立的系统提供sql优化的能力。
- 有自己的非sql的查询语言的数据处理系统:calcite允许通过直接实例化关系运算符构建关系运算符树。优化完成后再转回数据系统自己的查询语言。
4. QUERY ALGEBRA
算子(Operators)
关系代数是calcite的核心。除了常见的表达数据操作的算子(比如filter、project、join),calcite还包括不同目的的额外算子,比如能够更简洁表达复杂操作、更有效地识别优化机会。
特性(Traits)
calcite使用同一个实体表达逻辑算子和物理算子。只不过物理算子中使用traits描述物理属性。这些traits帮助优化器评估不同方案的成本。
在优化阶段,calcite会尝试在关系表达式上执行某些traits。关系运算符可以实现covert接口,指示如何将表达式从一个值转换为另外一个值。
优化不必要的计划:calcite包含关系表达式生成的数据的物理属性的通用traits,比如ordering、grouping、partitioning。calcite优化器会推理这些属性并利用他们避免一些不必要的计划。比如如果排序运算符输入的数据已经正确排序——可能是后端系统保证了顺序——那么排序操作可以被删除。
不同数据引擎之间的优化:calcite的主要特性之一是调用协议特性(calling convention trait)。本质上traits代表将在其中执行表达式的数据系统。把calling convention作为一个trait,可以允许calcite在跨多个引擎的查询中执行优化。举个例子:
上面的这个查询从mysql和splunk中查询数据,过滤器(where)由于特定的适配器规则被分配到splunk中执行。后面的数据join、group、sort可以基于spark实现:join被转换为spark协议,join的两个输入是jdbc-mysql到spark协议的convert和splunk到spark协议的convert。但是有一个更加优化的方案:因为splunk可以通过ODBC直接查询mysql,规划器规则可以将join上推到splunk,采用splunk协议实现join,在splunk引擎内执行。
总结:calcite中提供了常见的表达式操作的算子(filter、join),也提供了额外的算子以实现简化或优化等目的。算子中用traits表示物理属性。执行过程中calcite可以通过推理属性避免不必要的计划,或者在不同的计算引擎之间进行优化。
5. ADAPTERS
适配器(adapter)是一个定义了calcite如何为多个数据源提供通用访问的架构模型。一个adapter包括model、schema、schema factory:
model:访问数据源的物理信息的规范。
schema:model中数据(格式和布局)的定义。数据本身通过table访问。
schema factory:从model中读取meta信息生成schema。
当一个查询执行时,calcite调用adapter中定义的table接口去读数据。adapter可以往planner中添加一系列的rules。比如通常会包含一些规则将多个逻辑的关系表达式转换为adapter协议的相关表达式。
如第 4 节所述,Calcite 使用称为调用约定的物理特征来识别对应于特定数据库后端的关系运算符。这些物理运算符实现了每个适配器中底层表的访问路径。当一个查询被解析并且转换为关系代数表达式的时候,会为每一个table创建一个运算符(operator),这是adapter需要实现的最小接口。如果adapter已经实现了table scan operator,calcite的客户端的operator就可以对这个table进行任意的sql查询,比如sorting、filtering、joins。
为了扩展适配器提供的功能,calcite定义了个enumerable调用协议。具有可枚举调用约定的关系运算符只是通过迭代器接口对元组进行操作。这种调用约定允许 Calcite 实现在每个适配器的后端可能不可用的操作符。例如, EnumerableJoin 运算符通过join by其子节点的collecting rows并join on 所需属性。
对于小数据量处理的场景,table scan的比较低效。可以使用相同的基于规则的优化器来实现特定于适配器的优化规则。例如,假设查询涉及对表进行过滤和排序。可以在后端执行过滤的适配器可以实现匹配 LogicalFilter 的规则并将其转换为适配器的调用约定。此规则将 LogicalFilter 转换为另一个 Filter 实例。这个新的过滤器节点具有较低的相关成本,允许 Calcite 优化跨适配器的查询。
适配器的使用是一种强大的抽象,它不仅可以优化特定后端的查询,还可以跨多个后端进行查询。 Calcite 能够通过将所有可能的逻辑下推到每个后端,然后对结果数据执行joins和aggregations来实现涉及跨多个后端的表的查询。实现适配器可以像提供表扫描运算符一样简单,也可以涉及许多高级优化的设计。关系代数中表示的任何表达式都可以下推到具有优化器规则的适配器。
总结:adapter是calcite和backend的适配器,adapter中可以实现最小的table scan接口,这样calcite就可以在table上进行sql查询。另外adapter还可以提供个独立的优化规则,以便calcite实现表达式下推等优化。
6.QUERY PROCESSING AND OPTIMIZATION
Planner rules
calcite包含一些系列(上百个)planner rules去对表达式树进行不修改语义的转换。也允许数据处理系统复属于自己的优化规则。
实例1:
Calcite 为 Apache Cassandra 提供了一个适配器,这是一个宽列存储,它按表中的列子集对数据进行分区,然后在每个分区内根据另一个列子集对行进行排序。配器将尽可能多的查询处理下推到每个后端以提高效率是有益的。将 Sort 推入 Cassandra 的规则必须检查两个条件:
(1) 该表先前已被过滤到单个分区(因为行仅在一个分区内排序)
(2) Cassandra 中分区的排序与所需的排序有一些共同的前缀。
这需要将 LogicalFilter 重写为 CassandraFilter 以确保将分区过滤器下推到数据库。规则的效果很简单(将 LogicalSort 转换为 CassandraSort),但规则匹配的灵活性使后端即使在复杂的场景中也能下推算子。
实例2:
SELECT products.name, COUNT(*)
FROM sales JOIN products USING (productId) WHERE sales.discount IS NOT NULL
GROUP BY products.name
ORDER BY COUNT(*) DESC;
因为filter(sales.discount)旨在sales表中,可以将filter下推到join之前。如果sales和products是不同的后端数据系统,将fliter下推join之前后,还可以将filter推入后端数据系统执行(借助FliterIntoJoinRule实现)。
总结:rule对表达式树进行不修改语义的优化。
Metadata providers
metadata的两个主要目的:
1.指导规划器降低整体查询计划成本。
2.应用规则时提供信息。
calcite的默认mdetadata provider的函数,包括:
- 子表达式的总成本
- 该表达式的结果行数
- 数据大小
- 最大并行度
等。
另外可以提供plan的结构信息,比如某个树下方是否存在过滤条件。
calcite提供接口运行所有数据处理系统插入自己的metadata信息到框架。这些系统可以复写已经存在的函数,也可以提供自己的新的metadata函数,以便在优化阶段使用。
calcite的metadata provider是可插拔的,使用java轻量编译器Janino进行编译和实例化。它们的实现包含了数据的缓存,以便提供性能。
总结: metadata provider的目的是提供plan信息以及数据及统计信息,指导优化。
Planner engines
Planner engines的目标:触发提供给引擎的优化规则直到达到优化目标。目前calcite有两种不同的引擎:
1.基于成本的规划器引擎。该引擎使用类似于Valcano的动态编程算法来创建和跟踪通过触发不同规则生成的执行计划。最初,每个表达式都向规划器注册,以及基于表达式属性及其输入的摘要。当在表达式 e1 上触发规则并且该规则生成新的表达式 e2 时,规划器会将 e2 添加到 e1 所属的等价表达式 Sa 集合中。此外,规划器为新表达式生成一个摘要,与之前在规划器中注册的那些进行比较。如果找到与属于集合 Sb 的表达式 e3 相关联的类似摘要,则规划器已找到重复项,因此会将 Sa 和 Sb 合并为一组新的等价项。该过程一直持续到规划器到达配置的目标。它可以彻底探索搜索空间,直到所有规则都应用于所有表达式。也可以当计划成本没有提高超过给定阈值时,使用基于启发式的方法停止搜索。默认的成本函数实现结合了对给定表达式使用的 CPU、IO 和内存资源的估计。
参考:Drill/Calcite查询优化系列之2:Calcite的理论基础Volcano模型简介
2.穷举规划器引擎。它穷举地触发规则,直到它生成一个不再被任何规则修改的表达式。该规划器有助于快速执行规则,而无需考虑每个表达式的成本。
用户可以基于需要选择其一,也可以在优化过程的连续阶段应用不同的规划集。
总结:Planner engines的功能是触发规则达到优化目标。主要有两类引擎:基于成本的规划器引擎和穷举规划器引擎。
Meterialized views
多个 Calcite 适配器和依赖 Calcite 的项目都有自己的物化视图概念。例如,Cassandra 允许用户根据系统自动维护的现有表定义物化视图。这些引擎将它们的物化视图暴露给 Calcite。然后优化器有机会重写传入的查询以使用这些视图而不是原始表。
Calcite提供了两种基于物化视图的重写算法的实现:
- 基于物化视图替换:用物化视图的等价表达式替换部分关系代数树。
- 基于lattices:一旦数据源被声明为形成一个lattice,Calcite 将每个物化表示为一个tile,优化器可以使用它来回答传入的查询。对星型组织的数据源特别有效,但是因为对底层架构设施加了限制所以跟具限制性。
总结:calcite提供两种物化视图重写算法实现:基于物化视图替换和基于lattices。
7. EXTENDING CALCITE
Calcite不仅适用于SQL处理,还提供扩展能力。
- 对SQL的扩展,表达对其他数据抽象的查询
- 半结果化数据
- 流
- 地理空间数据
- 语言集成查询
7.1 Semi-structured Data
Calcite 支持多种复杂的列数据类型,可以将关系和半结构化数据混合存储在表中。具体来说,列可以是 ARRAY、MAP 或 MULTISET 类型。此外,这些复杂类型可以嵌套,因此可以有一个 MAP,其中的值是 ARRAY 类型。可以使用 [] 运算符提取 ARRAY 和 MAP 列中的数据(以及其中的嵌套数据)。不需要预定义存储在任何这些复杂类型中的特定类型的值。
例如,Calcite 包含一个适用于 MongoDB [36] 的适配器,这是一个文档存储,存储由大致相当于 JSON 文档的数据组成的文档。为了向 Calcite 公开 MongoDB 数据,为每个文档集合创建了一个表,其中包含一个名为 _MAP 的列:从文档标识符到其数据的映射。在许多情况下,可以期望文档具有共同的结构。代表邮政编码的文档集合可能每个都包含带有城市名称、纬度和经度的列。将此数据公开为关系表会很有用。在 Calcite 中,这是通过在提取所需值并将它们转换为适当类型后创建视图来实现的:
SELECT CAST(_MAP['city'] AS varchar(20)) AS city,
CAST(_MAP['loc'][0] AS float) AS longitude ,
CAST(_MAP['loc'][1] AS float) AS latitude
FROM mongo_raw.zips;
通过以这种方式定义的半结构化数据视图,可以更轻松地操作来自不同半结构化源的数据与关系数据。
总结:calcite支持复杂的列数据类型(ARRAY/Map/MULTISET),支持使用[ ]运算符获取ARRAY/MAP中的数据,还可以基于该运算符把半结构化数据转换为结构化数据,以便进行后续的关系查询。
7.2 Streaming
Calcite基于标准SQL提供了一组针对流的扩展:
- STREAM扩展
- 窗口扩展
- 隐式的窗口表达式(JOIN中)
STREAM扩展:带stream关键字,表示对流动的(incoming)数据进行计算;不带stream关键字,表示对已有的数据进行计算。例子:
SELECT STREAM rowtime , productId , units FROM Orders
WHERE units > 25;
窗口扩展的语法类似flink,包括over window、tumble window、hopping window、 session window等。over window的例子:
SELECT STREAM rowtime ,
productId ,
units ,
SUM(units) OVER (ORDER BY rowtime
PARTITION BY productId
RANGE INTERVAL '1' HOUR PRECEDING) unitsLastHour FROM Orders;
tumble window的例子:
SELECT STREAM TUMBLE_END(rowtime , productId ,
COUNT(*) AS c, SUM(units) AS units
INTERVAL
'1' HOUR) AS
rowtime ,
FROM Orders
GROUP BY TUMBLE(rowtime, INTERVAL '1' HOUR), productId;
join中隐式窗口表达式的例子:
SELECT STREAM o.rowtime , s.rowtime AS shipTime
FROM Orders AS o JOIN Shipments AS s
o.productId ,
o.orderId ,
ON o.orderId = s.orderId
AND s.rowtime BETWEEN o.rowtime AND o.rowtime + INTERVAL '1' HOUR;
总结:calcite提供了针对流的扩展,包括stream关键字、window和隐式window表达式。
7.3 Geospatial Queries
Calcite初步实现了了对地理查询的支持,核心是添加了一种新的GEOMETRY数据类型,该类型封装了不同的几何对象,例如点、曲线、多边形。Calcite的目标是完全符合OpenGIS Simple Feature Access 规范(定义了访问地理空间数据的 SQL 接口标准)。示例查询查找包含阿姆斯特丹市的国家/地区:
SELECT name FROM (
SELECT name ,
ST_GeomFromText('POLYGON((4.82 52.43, 4.97 52.43, 4.97 52.33, 4.82 52.33, 4.82 52.43))') AS "Amsterdam",
ST_GeomFromText(boundary) AS "Country"
FROM country
7.4 Language-Integrated Query for Java
Calcite 为 Java(或简称 LINQ4J)提供了语言集成查询,它严格遵循 Microsoft 的 LINQ 为 .NET 语言制定的约定。
8. INDUSTRY AND ACADEMIA ADOPTION
工业界使用Calcite的方式:
- 将calcite做为库,嵌入其核心。
- 实现adapter以允许calcite联合查询处理。
采用第一种方式的系统:
采用第二种方式的系统:
9. FUTURE WORK
Calcite 的未来工作将侧重于新功能的开发,以及其适配器架构的扩展:
- 对 Calcite 设计的增强以进一步支持其使用独立引擎,这将需要提供对数据定义语言 (DDL)、物化视图、索引和约束的支持。
- 对planner的设计和灵活性的持续改进,包括使其更加模块化,允许用户 Calcite 提供planner程序(组织成规划阶段的规则集合)以供执行。
- 将新的参数化方法应用到优化器的设计中。
- 支持一组扩展的 SQL 命令、函数和实用程序,包括完全符合 OpenGIS。
- 用于非关系数据源的新适配器,例如用于科学计算的数组数据库。
- 性能分析和检测的改进。
10. CONCLUSION
Calcite是一个SQL表示的,支持异构数据源的数据查询优化系统。Calcite将数据查询分为解析、验证、优化、执行四个阶段,在优化阶段提供了基于规则和基于消耗的优化能力。其架构实现上注重灵活性和可扩展性,使用者既可以只引入其中的某个模块,也可以通过实现adapter允许calcite联合查询处理。Calcite未来会朝着SQL命令扩展、功能增强、planner灵活性、更多适配器等方向发展。