目录
一、背景
二、查询优化器概述编辑
2.1 System R Optimizer
2.2 Volcano Optimizer
2.3 Cascade Optimizer
三、Join相关优化规则
3.1 JoinReorder
3.1.1 少量表的Reorder
3.1.2 大量表的Reorder
3.1.3 星型模型的Reorder
3.2 外连接消除
3.3 Join消除
3.4 谓词下推与谓词传递
3.5 聚合下推
3.6 Limit下推
四、应用BI系统实践
4.1 应用BI系统与OLAP引擎的区别
4.2 LogicalPlan与Expression
一、背景
在数据分析场景下,多个表的 Join 语句是很常见的。其中之一的原因为,在数据仓库的建设中,维度建模是一种常用的建设方法,因此数据仓库通常分为事实表和维表,但用户查询时需要完整的维度信息,因此会构建事实表和维表的join。同时,OLAP分析通常不会局限于一个独立的业务分析面,而是需要将业务相关的所有数据整合起来进行分析,此时Join功能起到了一个数据整合的能力。
应用BI系统也是数据分析的其中一个入口,虽然我们可以借助众多开源OLAP引擎的所提供的Join能力来实现多表Join,但以下两个场景仍然要求应用BI系统自身的引擎具备Join功能:
-
灵活自助分析中,用户可以选择任意选择感兴趣的数据进行整合分析,可能也包括用户本地上传的数据。这些数据可能不会存在同一个OLAP引擎中,甚至可能归属于不同类型的OLAP引擎。
-
OLAP引擎提供了秒级的查询性能,但受限于硬件能力,它所存储的数据不可能无限制增加。在实际业务场景中,OLAP数据库分库和拆分集群的情况是很常见的,这种情况下就无法依赖于OLAP引擎自身所提供的Join功能。
本文从应用BI系统的Join需求角度出发,首先对优化器的类型进行概要介绍,接着介绍了查询优化器中和Join操作具有关联关系的优化规则,最后从应用层的实际情况介绍应用层BI系统在实践过程中遇到的一些问题和思考。
需要说明的是,应用层BI系统的查询引擎选型不在本文的讨论范围,同时Join相关的优化并不仅仅指对Join操作进行处理的规则,还包含了可以跨越Join节点执行以提高Join效率的相关规则。同时优化规则是可以不断扩展的,本文列举的优化规则并不是理论上的全集,仅仅只是其中一部分。
二、查询优化器概述
查询优化器在数据库中一个重要且复杂的组件,它再一定程度上决定了数据库的性能。如上图,优化器接收一个被parser解析的逻辑算子的代数表达式树,优化器由一些从逻辑表达式树到最优等价物理表达式树的映射组成。换言之,优化器对算子重排序并选择实现算法。
数据库查询优化技术主要包括查询重用技术、查询重写规则、查询算法优化技术、并行查询优化技术、分布式查询优化技术及其他方面(如框架结构)的优化技术,这6项技术构成了一个“广义的数据库查询优化”的概念。一个优秀的查询优化器并不只会使用其中一种技术,而是需要从性能角度出发进行结合。但不同的查询优化器框架也具有不同的解决思路。这里我们不对具体的查询优化技术进行介绍,而是对于业界较为通用的三种查询优化器框架简要介绍其思路,以帮助我们更好地理解后续的Join相关规则。
2.1 System R Optimizer
System R 是一个具有开创性的项目,该项目是SQL 语言的第一次实现,从此SQL便成为标准关系型数据库查询语言。System R 也是第一个论证了关系型数据库管理系统能够提供良好的事务处理性能。System R 中的设计决策以及一些基本的选择算法(例如查询优化中的动态规划算法)对后来的关系型系统有巨大的影响。
System R将查询优化分为逻辑优化和物理优化两个阶段,逻辑优化根据规则对执行计划做等价变形,物理优化则根据统计信息和代价计算将逻辑执行计划转化为能更快执行的物理计划。同时System R的大牛Pat Selinger提出了Dynamic Programming和Interesting Orders这两个重要算法和思想,影响了所有后续的数据库优化器设计和实现。
System R 优化器的目标是在给定可用的数据结构和访问路径的情况下,找到一种低成本的方法来执行SQL语句。
System R在使用Dynamic Programming构建最低执行代价的物理执行计划时使用的是自底向上的算法,这一点和Volcano和Cascade不一样。自底向上的算法会先计算基表的访问路径(Access Path),通常来说存在几种:顺序扫描、索引扫描、组合索引等,而存在多个索引时,每个索引都视作一个访问路径。接着,枚举两表Join,这里同时还需要对Join的物理实现进行枚举,所以第二层的状态会比第一层多许多。一层层往上搜索,即可得到多表Join的执行计划。
在搜索过程中,每一层不需要保留所有的组合,而是保留代价最低的即可。但需要考虑到一个问题,两表Join的最优解,未必能得到三表Join的最优解,例如两表用了HashJoin,那么输出的结果会是无序的;相比之下,如果用MergeJoin,两表Join可能不是代价最小的, 但是在三表Join时,就可以利用其有序性,对上层的Join进行优化。
为了解决这个问题,引入了Interesting Order,即上层对下层的输出结果的顺序感兴趣。因此自底向上枚举时,A JOIN B不仅仅是保留代价最小的,还需要对每种Interesting Order的最小代价的Join进行保留。如果元组顺序是由查询指定的,那么它是Interesting的。一些查询可能没有指定感兴趣的顺序(即返回无序的结果)。为了优化连接成本,需要在产生Interesting order的最优方法和产生无序结果加上排序成本的最优方法之间进行选择。注意,可以将多路连接流水线化。
虽然 System R 优化器框架大大提升了数据库处理复杂 SQL 的能力,但也存在一定缺陷,比如:
-
扩展性不好。每次添加优化规则都需要考虑新的规则和老的规则之间的关系,需要对优化器非常了解的同学才能准确判断出新的优化规则应该处在什么位置比较好。另外每个优化规则都需要完整的遍历整个逻辑执行计划,添加优化规则的心智负担和知识门槛非常高。
-
搜索空间有限。搜索空间一方面因为优化规则难以添加导致比较狭小,另一方面,逻辑优化要求该优化规则一定在各个场景下都有收益才行,但在数据库面临的各种场景中,总有一些优化规则在某种数据分布下有收益,在另一种数据分布下没有收益,需要根据数据的分布估算代价来判断是否启用这些优化规则,因为这个原因,进一步导致一些优化规则不能添加到这个搜索框架中,或者添加后需要人工的通过开关来开启或关闭该优化规则。
2.2 Volcano Optimizer
Volcano/Cascades Optimizer 是经典的优化器框架,作者都是 Goetz Graefe。Volcano和Cascades Optimizer是当前业界最常用的两种框架,开源SQL引擎Calcite就实现了Volcano Optimizer。其中Cascades Optimizer是基于Volcano Optimizer基础之上的进一步优化,我们将会在后一章进行介绍。
Volcano Optimizer遵循了以下5个设计原则:
-
查询处理都是基于关系代数。需要定义代数算子、代数等价定义以及合适的实现算法。选择合适的算法就是查询优化。因此Volcano optimizer定义了两个类别的代数:逻辑代数和物理代数,将逻辑代数映射到物理代数的过程就是优化器,过程中使用了逻辑代数的转换和从逻辑到物理代数的基于代价的映射。
-
使用规则和模式来处理等价转换。规则被定义为关于模式的特定知识表达。查询优化中等价变换所需要的代数定律知识可以很容易地用模式和规则来表示。另外,我们需要关注规则的独立性,它保证了规则的模块化,优化过程中它被搜索引擎组合起来并独立地执行转换。查询优化是数据库系统中最复杂的一部分,模块化有利于后续的维护和扩展。
-
优化过程保证代数等价。优化器讲一个查询转换到最优的执行计划,需要保证代数等价。因为简单,尽量避免中间层次。
-
使用编译执行,而不是解释执行。解释可以带来更多灵活性,但编译可以带来更好的性能。查询优化过程是CPU密集的,所以采用编译执行。同时为了增加扩展性,可以让rule或是条件接收参数。
-
基于动态规划实现搜索。
在Volcano Optimizer中,决定了它的功能性和扩展性的两个核心模块为:优化规则、搜索引擎。
优化规则Rule分为两类:代数等价规则,例如交换律和结合律,称之为transformation rules;算子到算法的映射,称之为implementation rules。rule需要支持复杂映射(implementation),例如join+project可能就映射为一个算法,也可以将一组逻辑算子映射为一个单独的物理算子。Rule的执行通过模式匹配来实现。每个rule都有它对应的pattern,只有在匹配成功后rule才会执行。
由于数据库查询优化的一般范式是创建等价查询评估计划,然后在许多可能的计划中进行选择,因此搜索引擎及其算法是查询优化器的核心组件。Volcano优化器提供了一个搜索引擎,使用自底向上的动态规划,对比System R优化器中只能处理关系型select-project-join查询的自底向上动态规划相比,Volcano优化器将动态规划从关系连接优化扩展到一般代数查询和请求优化。为了避免表达式和plan被重复地进行等价转换,volcano optimizer使用一个hash table来存储表达式和等价类,等价类分为两个集合,等价逻辑表达式和等价物理表达式。
Volcano Optimizer论文中,作者主要将它和EXODUS优化器(也是作者本人提出的,和SystemR比较类似)进行了对比,主要体现在以下两个大方面:
-
功能性和扩展性
-
Volcano优化器区分了逻辑表达式和物理表达式,更清晰。
-
Volcano优化器结合了物理属性和逻辑表达式来一起进行优化,对于优化器搜索引擎的效率有了很大提升(例如结合中间结果的有序性这一物理属性进行优化)
-
Volcano优化器搜索算法是自上而下的,可以充分利用各种剪枝策略,子表达式只有在必要的时候才会进行优化
-
Volcano对成本数据结构进行了更通用的定义
-
Volcano优化器扩展性更好,通过搜索引擎中维护的等价表达式和各种搜索策略来实现。
-
-
搜索引擎的效率和效果:Volcano优化器不仅扩展性好,而且更高效。
2.3 Cascade Optimizer
Cascade优化器是基于Volcano优化器基础之上设计的,应用了许多在Volcano优化器中提出的可扩展查询优化、并行查询执行和物理数据设计等相关设计要点,并解决了Volcano优化器遇到的一些问题。和Volcano优化器对比,Cascade优化器具有以下特点:
-
优化算法的Task拆分:优化算法被分为多个部分,称之为任务Task。将优化算法切分Task提供了更高的灵活性,Task对象可以进行重排序,为启发式引导提供了更灵活的机制。如下图。
-
废弃两阶段优化:在Volcano优化器,第一步是执行所有的transformation rules来为一个查询和它的子树来创建所有可能的逻辑表达式,第二步才会执行实际的优化,根据等价表达式的指引执行implementation rules来获取最佳执行计划。而Cascades优化器废弃了这样的两阶段优化策略,因为派生出所有的逻辑等价表达式是没有用的。一个Group值需要按需使用transformation rules即可,并且它只需要创建匹配了给定pattern的group的成员。
-
搜索算法优化:
-
避免重复的执行:Cascades优化器使用一个bitmap来为每一个节点标明哪些transformation rules已经被执行了,不应再重复执行。
三、Join相关优化规则
通过前面我们对优化器框架的概述,我们可以了解到,搜索引擎中动态规划算法是优化器寻找最优执行计划的方式,而表达式的等价变化才能构成搜索空间,再结合cost函数或是启发式规则,搜索引擎才能在搜索空间中做出选择。在这里我们暂不对优化器中复杂的搜索引擎进行细究,本章我们主要讨论Join相关优化规则中的表达式等价变换。
3.1 JoinReorder
在业界的查询引擎中,Join的执行效率受各个表的存储、索引信息影响,JoinReorder主要考虑底层存储引擎的数据量级和索引信息来进行重排序,目标是让中间结果更小。
在进行多表Join操作时,根据表连接的方式,我们可以用连接树来展示。连接树是将关系名和连接操作的代数表达式的直观表示。它是一个二叉树,所有叶子结点都是不同的关系,而内部则是连接操作(或者点乘操作)。连接树根据其形状可以分为以下几种:
重排序操作即为将查询树中各节点的位置进行等价交换,以得到最优连接树。注意,Join重排序的基础在于连接操作之间是符合交换律和结合律的,但连接操作的交换律和结合律是有限制的:
-
inner join满足交换律和结合律
-
full outer join满足交换律和结合律
-
left/right outer join 在进行交换时,需要修改join类型,不满足结合律,但left outer join交换为right outer join后不会影响执行过程(?)
受限于join交换律的限制,多个业界查询引擎/DB在进行JoinReOrder时都限制了所处理的类型,例如TiDB和Spark只对inner like join()进行了重排序处理。
JoinReorder通常根据join表的数量选择实现算法,因为join表越多,可能的组合就会越多。因此,通常情况下,JoinReOrder的算法会根据表数量进行选择,当表的数量较少(一般以<=5为阈值)时,通过交换律和结合律来进行优化,当表数量>5时放弃找到最优解,采用贪心算法/动态规划实现。
3.1.1 少量表的Reorder
在表数量较少时,通过交换律和结合律实现也有不同方式:
-
Calcite:Calcite作为一个SQL引擎,实现者能够为Calcite提供足够多的的元数据信息(索引/统计),Calcite的cost函数实现更为简单。因此Calcite通过几个固定pattern的rule来实现少量表的Reorder:
-
JoinCommuteRule:直接对join两表进行交换,代价驱动
-
JoinAssociateRule:匹配inner join左深树,结合join/where条件来对节点进行结合律转换。(A join B) join C => A join (B join C)
-
JoinPushThroughJoinRule:匹配inner join左深树,根据代价进行交换律转换。(A join C) join B => (C join B) join A
-
-
Spark:Spark作为一个查询引擎,没有内置的存储层,而是通过connector对接不同的数据源,因此有可能获取不到完整的元数据,当前Spark CBO相关的元数据是通过任务直接查询获取的,代价较大。(猜测这可能也是Spark2.4中默认关闭CostBasedJoinReorder规则的原因)。Spark的JoinReorder有两个规则:ReorderJoin、CostBasedJoinReorder,但它们并不是根据表数量拆分处理,CostBasedJoinReorder由于搜索空间的问题仅支持对小于12个表的join进行处理。猜测当前可能处于过渡期,后续如果能解决元数据问题可以完全切换到CostBasedJoinReorder。
-
ReorderJoin:将join表组成一个列表,按照join条件从列表中逐步过滤出符合要求的表,并构建Join节点
-
因此,少量表的Reorder,可以在符合交换律和结合律基础上,根据引擎的实际情况进行安排。
3.1.2 大量表的Reorder
在表较少时,我们通过固定patten的rule,理论上可以尝试所有的join组合,并从中选择最优的结果。但表数量过多时,用这种方法代价太大,因此一般采用贪心算法/动态规划算法来进行reorder。Spark的CostBasedJoinReorder使用了动态规划的算法,参考了Access Path Selection in a Relational Database Management System这一篇论文。具体步骤如下:
-
将所有的关系(表)放在level0层
-
在level0层之上构建所有的2表join
-
在前一层基础上构建所有的3表join(2表join再加一个独立的表)
-
依次类推,直到所有的表都构建了join,在从中挑选最佳的执行计划
在构建多路join时,Spark只保留相同元素集合的最佳执行计划(即cost最低的执行计划),例如对于3表join,Spark只会基于集合{A,B,C}来从执行计划集合{(A join B) join C, (A join C) join B, (B join C) join A} 中选择cost最低的一个执行计划。
3.1.3 星型模型的Reorder
但从数仓建设考虑,我们在查询时,星型模型出现的概率是极高的,以星型模型的查询图为例,我们可以发现,当星型模型的join类型为inner或是left outer时,维表的顺序其实是可以任意调换的。当然,如果星型模型中join类型全部为left outer(且不可消除outer),那么调换维表顺序本质上对查询效率是没有影响的。但如果星型模型中存在join类型inner,那么我们将数据量更小,或是通过索引选择率判断出结果数据会更小的表交换到前面可以让中间结果数据量变小,进而提升join效率。
Spark针对inner过程中的子树星型模型进行了启发式重排序,将fact表放在左深树的叶子节点,避免了内部的大表访问,有利于hash join效率;同时将选择率高的维表放在执行计划的前面以减少中间数据。但也限制了inner join。
从数仓和SQL理论来看,left outer join的星型模型也是可以这样优化的,但Spark并未采用,原因未知,后续更深入地理解相关原理后再进行研究。
3.2 外连接消除
在很多查询场景下,我们可以将外连接(left outer/ right outer/ full outer)进行一定程度的消除,外连接消除后join所生成的中间数据会更小,效率更高,也更有利于后续的谓词下推。外连接消除是一个启发式的规则,属于确定的可以进行优化的规则,无需参与动态规划。
什么情况下外连接可以转内连接?左向外连接的结果集包括左表的所有行,而不仅仅是连接列所匹配的行。如果左表的某行在右表中没有匹配的行,则在结果集右边补 NULL。做谓词下推时,如果我们知道接下来的的谓词条件一定会把包含 NULL 的行全部都过滤掉,那么做外连接就没意义了,可以直接改写成内连接。
什么情况会过滤掉 NULL 呢?比如,某个谓词的表达式用 NULL 计算后会得到 false 或者 NULL;或者多个谓词用 AND 条件连接,其中一个会过滤 NULL;又或者用 OR 条件连接,其中每个都是过滤 NULL 的。
具体判断条件如下:
-
left outer
-
如果右表具有消除其空值行的谓词,则可以转为inner join
-
-
right outer
-
如果左表有消除其空值行的谓词,则可以转为inner join
-
-
full outer
-
如果左右表都有消除空值行的谓词,则可以转为inner join
-
如果左表有消除空值行的谓词,则可以转为left outer
-
如果右表有消除空值行的谓词,则可以转为right outer
-
以left outer join为例,下面的SQL是等价的:
select * from t1 left outer join t2 on t1.id = t2.id where t2.id = 1
select * from t1 inner join t2 on t1.id = t2.id where t2.id = 1
3.3 Join消除
在一些场景下,我们虽然定义了Join关系,但是查询结果集却完全不受其中某个表的影响,这种情况我们可以对Join进行消除。消除规则如下:
-
只能处理outer join,因为inner join可能会在join过程中执行一些结果集的过滤,无法预知是否某个表会有影响。
-
只可能消除outer join的右表
-
若outer join,join key是右表的unique key,且结果集不包含右表的非join列,则可将结果集全部切换为从左表获取,且消除右表
-
若outer join,且查询结果只对左表结果进行了一些去重操作,如distinct,count( distinct),那么可以消除右表
select distinct t1.id from t1 left outer join t2 on t1.id = t2.id
3.4 谓词下推与谓词传递
关于谓词下推,它主要还是从关系型数据库借鉴而来,关系型数据中将谓词下推到外部数据库用以减少数据传输;属于逻辑优化,优化器将谓词过滤下推到数据源,使物理执行跳过无关数据。最常见的例子就是 join 与 filter 操作一起出现时,提前执行 filter 操作以减少处理的数据量,将 filter 操作下推。
随着OLAP存储层的发展,谓词下推也可以结合存储层的一些物理属性执行操作,例如,若存储层的索引能够执行全部或部分的谓词过滤,那么我们可以将谓词下推到TableScan物理算子节点,使其成为TableScanAndFilter,这一操作主要取决于存储层所支持的物理属性。
谓词下推的谓词包括Where子句中的谓词,也能包含ON条件中的谓词,同时在做跨Join节点的下推时,也需要考虑join节点的类型。下推规则如下:
按照hive优化器中的概念理解,join中有4个概念:
保留表:保留数据的表,left outer join中的左表即为保留表
NULL保留表:join时如果没有匹配上,需要使用NULL填充的表,left outer join中的右表即为NULL保留表
JOIN谓词:ON条件中的谓词,用于判断数据是否能关联上,不对最后结果做过滤
JOIN后谓词:Where子句中的谓词,在JOIN执行完成之后对结果进行过滤
对于谓词下推的规则即为:
-
如果是on后面的过滤条件,是不能提前到保留表过滤执行优化。
-
如果是where后的过滤条件,是不能提前到null值保留表过滤执行优化。
-
inner join:Where和ON子句中的左表谓词和右表谓词都可以分别下推到左右表。
-
left outer join:左表的where可以下推,右表的where不可以下推;左表的on不可以下推,右表的on可以下推。
-
right outer join:和left相反,可以先将right转为left进行处理
如果谓词下推时我们要区分左右表的谓词分别进行下推,不可将左表谓词下推到右表,但在查询场景中,如果ON条件中存在左右表的关联关系时,其实左表谓词是可以传递给右表的。如下:
select * from t1 join t2 on t1.id = t2.id where t1.id = 1
如果我们直接针对这一SQL进行下推,那么会将t1.id = 1下推给t1表,而t2表则需要扫描全表。但实际上t1和t2表的关联条件即为t1.id = t2.id,实际上t2表扫描出来的,id!=1的数据行,在关联时也无法关联上,是没有用的。在这个场景下,我们可以先执行谓词传递,将t1.id的过滤条件通过ON条件的关联关系传递给t2.id,这样t2表在进行扫描时可以使用id=1来进行过滤,减少中间数据量。
谓词传递的规则如下:
-
inner join:左表的谓词列,若和右表某一列为恒等关联ON条件,且在ON条件中是使用And进行关联的,那么左表的谓词列可以传递给右表。右表同样可以传递给左表
-
left outer join:左表的谓词列,若和右表某一列为恒等关联ON条件,且在ON条件中是使用And进行关联的,那么左表的谓词列可以传递给右表。
-
right outer join:与left相反,右表的谓词在符合条件的基础上可以传递给左表
3.5 聚合下推
聚合下推的根本目的和谓词下推比较类似,都是为了减少查询执行过程中的中间数据集大小,同时也会受到存储层物理属性影响,例如,若存储层支持了预聚合,那么聚合下推应该直接下推到存储层获取预聚合后的数据,而不是扫描数据后再做内存聚合。
聚合下推的原理在于:大部分的聚合函数是可以拆分的,除了Count Distinct和一些统计函数外。如下例,交易额在日期、城市两个维度上直接SUM的结果和现在城市上SUM再执行日期维度上SUM的结果是一样的。接下来我们主要考虑跨越Join节点的聚合下推。
关于聚合下推,我们需要注意的点是:
-
聚合操作的可拆分型:除去重外,其他聚合函数都可以拆分的,即先局部聚合再整体聚合,对于非去重的聚合函数是无影响的。
-
去重聚合的特殊性:对于去重来说,若局部聚合的维度就等于最终的聚合维度,那么也可以拆分,只是整体聚合时需要从去重函数改为MAX。
-
聚合下推时的维度处理:跨越Join节点的局部聚合需要考虑Join时的关联维度
因此,跨越Join节点的聚合下推的具体匹配条件和执行规则是这样的:
-
匹配条件:聚合函数中没有不可拆分的函数,如去重、统计等(如果下推维度完全一致也是可以的)
-
执行步骤:
-
拆分Aggregate和Join中的维度和指标,按照左右表区分,如下图,左表维度集合为[id],右表维度集合为[tag, id];左表聚合指标为[sum(price)],右表聚合指标为[count(ord_num)]
-
将左右表的维度和聚合指标分别下推到左右表,构建Aggregate
-
保留Join后的Aggregate,若左右表的下推Aggregate维度都和原始Aggregate维度相等,可以将Aggregate改为Project,否则需要原样保留Aggregate。
-
3.6 Limit下推
Limit在很多场景下都可以下推,例如Union All(去重的Union则不可下推)、Join、视图、窗口函数等等,但Limit的下推通常需要考虑Sort,如果有Sort,Limit下推通常会伴随着Sort下推,我们可以把Limit和Sort结合起来作为TopN处理,如果没有Sort,那么Limit相当于一个排序规则为空的 TopN 节点。这里我们只考虑跨越Join节点的Limit(TopN)下推,规则如下:
-
inner join:TopN不可下推,因为join过程可能对数据进行过滤,TopN下推后无法保证最终结果集数量符合N的要求。
-
left outer join:左表为保留表
-
如果TopN排序规则为空,则可以直接下推到左表,但不可下推到右表。
-
如果TopN排序规则为空,但仅依赖于左表,则TopN可以下推到左表
-
-
right outer join:与left相反,可先将right转为left处理
-
full outer join:TopN不可下推
四、应用BI系统实践
4.1 应用BI系统与OLAP引擎的区别
应用BI系统通常需要为用户提供高效、灵活的交互式分析,这样就要求了它需要在技术上具体以下几个特点:
-
快速的存储层访问,能提供秒级的查询效率
-
高效的查询引擎,支持复杂的查询模式
-
贴近业务的分析功能
因此,BI系统通常是构建在高效的OLAP引擎之上的,从系统整体结构来说BI系统中包含了OLAP引擎,但BI系统所提供的能力并不会受限于底层OLAP引擎的能力。我们暂时将OLAP引擎从应用BI系统的架构中抽离出来,对比它们之间的区别:
-
查询语言:
-
OLAP引擎:通常以标准SQL语言对外提供服务
-
BI系统:通常会将用户的分析行为抽象为DSL,为BI系统内部的前后端交互语言
-
-
查询引擎:
-
OLAP引擎:标准SQL查询引擎,具备SQL解析、执行计划构建、优化、调度等一系列通用模块。
-
BI系统:业务查询引擎,与SQL查询引擎类似,但需要更重视业务分析规则,而减少查询引擎中最复杂的优化,因为BI系统不是数据库,元数据不足,同时大部分的优化器功能可以交给OLAP执行
-
-
数据存储:
-
OLAP引擎:考虑分析性能,常用列式存储
-
BI系统:connector插拔式接入多种数据源,OLAP引擎即为BI系统底层存储之一。
-
4.2 LogicalPlan与Expression
应用BI系统是建立在OLAP引擎之上的,但它们都有查询引擎,区别在于OLAP查询更多关注标准关系型数据计算,应用BI系统的查询引擎更多关于BI业务需求。O
OLAP上的SQL查询在BI查询引擎执行计划中只是其中一个节点。这也就要求了应用BI系统在执行计划的拆分时,将一些底层计算行为转为SQL拼接。在OLAP引擎和应用BI系统中的(逻辑)执行计划抽象区分如下:
-
OLAP引擎:
-
LogicalPlan:逻辑执行计划,LogicalPlan最终会转为一个执行引擎可调度的Task,例如TableScan等。LogicalPlan中可能包含表达式Expression。
-
Expression:Expression是表达式体系,是指不需要执行引擎计算,而可以直接计算或处理的节点,包括Cast操作、Porjection操作、四则运算和逻辑操作符运算等等
-
-
应用BI系统查询引擎:
-
LogicalPlan:逻辑执行计划,同样会转为一个执行引擎可调度的Task。但BI系统不直接对接物理存储,不会有TableScan节点,它的叶子节点是一个执行SQL的LogicalPlan。
-
Expression:应用BI的Expression体系,除了一些函数计算外,还需要将结构化SQL拆分为Expression。因为BI系统需要具备SQL的构建能力,而很多用户分析需求是可以通过SQL的形变来实现的。
-