查询优化中的物化视图改写(三):快速匹配

330 阅读4分钟

我们在[1]和[2]中描述了如何从一个MV中计算出Query的结果,并且我们提到匹配算法是作为优化器的一个Rule执行的。通常在一个数据库中用户会创建或者维护多个MV来加速查询,每个 Query跟每个MV来做匹配会极大地降低查询优化的速度,因此我们需要提前过滤到完全不会匹配上的MV以加速查询过程。

Lattice Index

假设我们有一些节点,每个节点都是元素的集合。我们定义如下2个关系:

  1. minimum supersetminimum\ superset: V1V2V_1 \sub V_2并且∄Vi,whereV1ViViV2\not\exist V_i, where V_1 \subset V_i\land V_i \subset V_2
  2. minimum subsetminimum\ subset: 跟1的定义相似。

我们可以将这些互为minimum supersetminimum\ superset的节点用线连接起来,从而形成一张图。如下是一个例子:

Untitled.png

SPJG的过滤条件

有了lattice index,我们可以通过[3]中提到的一些条件快速过滤掉不匹配的MV。

  1. MV的base table集应该是Query的超集。根据这个条件,我们可以把每个MV视作一个节点,里面的元素是base table集,从而构建lattice index。在这个index中我们从tops开始查找,直到Query的base table不被某个节点包含即停止。
  2. 在[1]中提到我们可以将MV中的base table按照cardinality reserving join构建一张图,并且依次按条件删除边和点,直到没法再删了为止。最后留下的图的base table集我们称之为hub。显然如果一个Query能从MV中计算出来,那么这个MV的hub必然是Query的base table的子集。因此我们以每个MV的hub作为集合来构建lattice index,然后搜索的时候从roots出发即可。
  3. 假设SJPG 的grouping或者projection里直接使用了某个列,那么MV的output的列表里应该包含这个列,并且这里应该考虑到column equivalence class。比如我们的Query需要的列是{A,B,C}\{A, B, C\},而Query里column equivalence class是{A,D,E},{B,F},{C}\{A, D, E\}, \{B, F\}, \{C\}。而MV的column equivalence class是 {A,D,G},{E},{B},{C,H}\{\underline{A}, D, G\}, \{\underline{E}\}, \{\underline{B}\}, \{\underline{C}, H\},其中下划线的是输出列。那么MV的extended output是{A,D,G,E,B,C,H}\{A, D, G, E, B, C, H\}。我们以MV的extended output作为节点的集合来构建lattice index。在搜索的过程中从tops开始,直到找到不匹配的节点。注意这里的匹配不一定是列完全匹配,只需要Query的每个列的column equivalence class里有一个列匹配即可。
  4. 最后我们考虑范围顾虑条件。 假设我们的Query的column equivalence class仍然如上面所示,同时我们有B<10 and C >100B \lt 10\ and\ C\ \gt 100,那么有2个column equivalence class有范围过滤条件,我们将这2个class合并得到Query的column列表是{A,D,E,C}\{A, D, E, C\}。同时假设MV的范围过滤条件只有A>10A > 10,那么MV的范围过滤条件列是{A}\{A\} 。很显然只有Query列集合合并之后的集合是MV的条件过滤列的超集,我们才能从MV中计算出Query。因此我们使用MV的范围条件过滤列构建lattice index,同时并且计算出 Query的扩展范围条件过滤列,从roots开始查找即可。

Stacked MV的匹配

我们将构建在MV之上的MV称作Stacked MV。从理论上将Stacked MV的匹配与原来的base table的匹配没有本质的区别。但是[4]中提到SQL Server中的实现的cascade 优化器有一些问题会导致有些case被忽略。比如我们要匹配Q=ABCQ = A \bowtie B \bowtie C,现在有2个MV,其中V1=ABV_1 = A \bowtie BV2=ABCV_2 = A \bowtie B \bowtie C,那么其实Q有2个等价的变换V1CV_1 \bowtie CV2V_2。Cascades优化器会认为这2个变换是一样的,从而只考虑其中的一个。这个在有Stacked MV的情况下可能会有问题,比如我们有V3=V1CV_3 = V_1 \bowtie CV4=γ(V3)V_4 = \gamma(V_3),而Q=γ(ABC)Q^{'} = \gamma(A \bowtie B \bowtie C),那么如果如果QQ的匹配选择的V2V_2,那么就没法QQ^{'}就没有办法选择到V4V_4,从而没法找到最好的优化。但是去除这个限制盲目的把所有的选项都枚举出来显然会降低效率。

为了解决上面提到的问题,[4]中提出了一个SPJG SignatureSignature的概念:SQ=[GQ;TQ;CQ]S_Q = [G_Q;T_Q;C_Q]

  • GQG_Q用于表示这是SPJ还是SPJG
  • TQT_Q是Query中引用到的base table或者MV的集合
  • CQC_Q是Query的candidate,对于一个MV来说,当TQTVT_Q \subseteq T_V是,VCQV \in C_Q

SPJG Signature的计算比较简单:

Untitled 1.png

有了计算每个Query的方法,那么计算Memo中Group Expression e的Signature Set的方法就比较简单了:

Σe={Stttrees(e)}\varSigma_e = \{S_t | t \in trees(e)\}

其中trees(g)trees(g)是以gg为根节点的等价变换的集合。计算Σg\varSigma_{g}的算法如下:

Untitled 2.png

其中tt表示单独的base table或者MV,α\alpha表示一元操作符(Selection, projection, group)等,β\beta表示二院操作符(join)。

而对于 Memo中一个Expression Group g的Signature Set可以用如下方法表示:

Σg={Σeeg}\varSigma^{g} = \{\varSigma_{e}| e \in g\}

在优化器进行exploration的过程中,Σe,Σg\varSigma_{e}, \varSigma^{g}需要进行动态地维护。

下面我们使用一个例子来演示这个过程,假设A,B,C,DA, B, C, D是base table,同时我们有如下的几个MV:V1:=BD,V2:=ABCD,V3:=γ(CV1)V_1 := B \bowtie D, V_2 := A \bowtie B \bowtie C \bowtie D,V_3 := \gamma(C \bowtie V_1),如下图所示可以展示变换和维护的过程:

Untitled 3.png

Untitled 4.png

有了SPJG Signature,我们在匹配BCDB \bowtie C \bowtie D的过程中可以匹配到CV1C \bowtie V_1这个选项,从而最终匹配到V3V_3

参考文献

  1. 查询优化中的物化视图改写(一):SPJG的改写
  2. 查询优化中的物化视图改写(二):SPOJG的改写
  3. Optimizing Queries Using Materialized Views: A Practical, Scalable Solution, 2001
  4. Stacked Indexed Views in Microsoft SQL Server, 2005