On the Correct and Complete Enumeration of the Core Search Space

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
简介: 在之前的文章中我们讨论了基于graph的DP-based算法,来解决join ordering的枚举问题。这些DP算法通过join predicate描述的连通性,解决了枚举可能的表组合问题,但join graph本身(即使hypergraph)是无法完整的描述join语义的,因为连通边本身无法描述不同类型的join语义,例如left outer join/semi join/anti join...,因此即使找到了所谓的csg-cmp-pair,也不一定是有效的plan。这篇paper讨论的就是这个问题,当枚举出一个csg-cmp-pair (S1 o S2),如何判断这是有效的join

在之前的文章中我们讨论了基于graph的DP-based算法,来解决join ordering的枚举问题。

这些DP算法通过join predicate描述的连通性,解决了枚举可能的表组合问题,但join graph本身(即使hypergraph)是无法完整的描述join语义的,因为连通边本身无法描述不同类型的join语义,例如left outer join/semi join/anti join...,因此即使找到了所谓的csg-cmp-pair,也不一定是有效的plan。

这篇paper讨论的就是这个问题,当枚举出一个csg-cmp-pair (S1 o S2),如何判断这是有效的join组合?其中涉及关系代数等价变换,数学符号略多。。。

基本术语

  • LOP

表示逻辑的二元算子,本文的上下文中就是Inner/outer/semi/anti join,本来还有讨论group join,但由于是HyPer系统下的特定优化,因此就先不考虑它了。

  • F(e)/ image.png

分别表示一个表达式e,其中引入的属性集合,以及属性所属于的表集合

  • STO(o) / T(o)

分别表示以算子o为根的子树中,所有children算子的集合/子树中表的集合

  • SES(o) (syntactic eligibility sets)

从语法上,算子o可以执行,需要依赖的表集合,也就是这些表都存在时,算子o才能计算

  • Degenerate Predicates

如果在二元算子中的谓词,但没有同时引用算子两侧的表,也就是

T (left(o)) ∩ FT(p) = ∅ ∨ T (right(o)) ∩ FT(p) = ∅,注意笛卡尔积也属于这种情况

Core Search Space

做优化器的同学离不开join ordering这个最基本却又最重要的功能,但并没有一个规范化的描述,到底ordering是什么意思?

从总体来看,join ordering就是利用各种join的语义,建立可以生成等价plan的transformation,也就是变换后的join结果与变换前一致。通过不断穷尽式的应用这些transformation,来生成各种可能的有效的join顺序,就是join ordering了。

其中主要包含3种transformation:

commutativity 交换律

交换律只针对单一算子,其含义显而易见

v2-7be1ba36f2ba16c2abedbed1659840ed_b.png

注:上图最后一个是group join,可以先忽略它,+表示交换律可以成立,-表示不成立,满足交换律记为comm(o)

associativity 结合律

结合律针对2个算子,具体来说满足

image.png


这样的恒等式,则结合律成立,这里operator image.png的下标表示它只引入了e1和e2中的属性,不包含e3的,很明显如果包含e3属性,等式的左边根本无法执行,而右边则可以。

如果上述等式可以成立,则标记为assoc(◦a, ◦b),即oa, ob满足结合律,注意这里是有顺序的,oa -> ob满足结合律,不等价于ob -> oa满足结合律,因此结合律不具有对称性!但如果oa, ob都满足交换律,则存在这种对称性,可以如下推导

image.png

下表给出了不同算子的组合之间,是否满足结合律

image.png

注:首先观察上表并不是对角线对称的,说明assoc本身不对称,同时对于有上标的+号,表示有条件的成立,即必须满足标注的条件,结合律才可以成立。

asscom 交换结合律

仅是交换律+结合律并不能完全描述所有等价变换,例如下面这种

v2-f0b264715f97574fc92f90992e4e7507_b.png

最常见的semi-join变换方式,无法用以上两种变换描述。因此引入这种:

image.png

因为是发生在top operator ob的左子树,记为l-asscom(oa, ob)

同样有

image.png

发生在top operator ob的右子树,记为r-asscom(oa, ob)

注意asscom是具有对称性的:

v2-4ed0137a55661d70df3bc498103d26ea_b.png

同样asscom可以用交换律和结合律推导出来:

image.png

针对各种operator变换的合法性如下表所示

image.png

可以看到,这个table是沿对角线对称的。

在table 1 - 3的各个entry中,标记为 - 的,或者上标中的条件无法满足时,则代表一个conflict,表示如果做了这种变换,生成的plan是非法的。

用更加直观的operator tree来描述等价变换的要求

image.png

上图的左侧部分,每种变换的下方2个表达式,描述了允许变换的前提条件,以第一个为例,可以执行右侧的等价变换,必须要求ob中不能引用e1的属性,oa不能引用e3属性,这个是很明显的前提。

不太明显的是,如果一个算子的谓词p,不是那种degenerate谓词(即同时引用了左右子树的属性),则assoc和l/r - asscom不可能同时成立,例如前2个变换如果同时满足,则ob的谓词pb同时不引用e1和e2的列,也就是不引用左子树的列,与其不是degenerate谓词矛盾了。

core search space

基于一个初始operator tree,穷尽式的应用以上所有可行的transformation生成的有效plan集合,构成了core search space,以 image.png这个简单算子树为起点的core search space如下:

image.png

可以通过对这4种transformation的应用做限制,来实现对于join search space的约束,例如

Left Deep Tree:

只允许对最左下方的join应用comm A⨝B → B⨝A 和 l-asscom (A⨝B)⨝C → (A⨝C)⨝B 这两种变换。

Zig-Zag Tree:

允许任意join应用comm A⨝B → B⨝A 和 l-asscom (A⨝B)⨝C → (A⨝C)⨝B 这两种变换。

Bushy Tree:

允许任意join应用任意变换。

搜索空间大小:Bushy Tree > Zig-Zag Tree > Left Deep Tree

与plan enumeration算法结合

有了以上的那些抽象概念,怎么和plan enumeration算法结合呢?由于知道什么叫做conflict,那么每找到一个csg-cmp-pair,只要能判断其是否存在conflict,就可以淘汰掉非法的plan,保证算法的正确性。如下是对之前文章提到的DPsub算法的检测:

image.png

上面第10行的APPLICABLE 函数,就是所谓的检查conflict的函数,输入是 S1 o S2这个操作。

注意在上面的算法中,显示对每个算子的交换律做了考虑(line 12),因此APPLICABLE中主要考虑assoc + l/r-asscom这3个变换。

检测冲突

paper中采用了“3步走”的方法,依次提出了3种逐步完善的CD(conflict detector),最终给出能够保证正确性(不会产生非法plan)且能够保证完备性(不会漏掉合法plan)的算法。

基本思路是利用上面的这些冲突表table 1 -3,在给定一个初始的join operator tree之后,对每个子树的顶层算子(用ob表示),其所拥有的子树可能会进行各种可能的变形,导致表达式e移入/移出,但由于有冲突的存在,会对能做的transform产生约束,因此对ob左/右子树中的每个算子oa,(oa ob)/(ob oa)禁止变换的冲突约束了ob的子树中,某些表达式一定要存在于子树中不能移出!!或者只能有条件的移出!!这种约束构成了对ob的一种检查,我们可以把所有存在的约束,描述成某种形式,用来对(S1 ob S2)进行检查,检查到约束不被满足,则这个join plan就是非法的。

这么说貌似比较抽象难以理解,具体看下3个步骤

Step 1: CD-A

这是最为粗暴的约束方式,针对每个operator引入了一个TES(total eligibility set)的概念,表示一个表的集合,这个集合最初只有SES(o),但随着检查(oa, ob) ,或(ob, oa)发现冲突,为了把冲突导致的无效形式从结果上避免掉,方法就是约束某些表达式e(table)必须在ob的子树下!!

TES就是由conflict所约束的必须在ob的子树下的表的集合,记为TES(ob) 。

image.png

以上图的第一个变换assoc为例,假设结合律变换是非法的,为了保证assoc(oa, ob)无法执行,采用了反向约束的方法,即限制e1一定要在ob下面,这样就无法做从左侧树 => 右侧树的变换了。l/r-asscom也是类似的思路。

同时要考虑一种情况

v2-e4027e149ad95d92ebecaaee1833af97_b.jpg

即存在冲突的两个算子Op1/Op2并不直接构成父子关系,但Op1在Op2子树下,如果子树自身允许做重排序,那么也会在变换后形成右图的形态,这种跨层级的冲突也要考虑,因此对TES的收集,需要自底向上进行:

image.png

通过如上算法建立整个operator tree中各个算子的TES集合后,在执行枚举算法时,每建立一个csg-cmp-pair (S1 o S2) 时,只需做如下检查:

image.png

L-TES就是TES中左子树的部分,R-TES是右子树的部分。条件成立则认为是合法csg-cmp-pair,否则非法。

注意这种方法确实可以避免无效的plan,但因为是反向的,会产生过大的限制,导致某些valid plan也无法生成,造成误伤。

image.png


上图中,由于semi-join0,1(最左下operator)与left-join2,3(最上方operator)无法满足assoc,CD-A会约束R0必须在left-join2,3的下面,导致Plan1 / Plan2的这种合法plan也没法生成。

Step 2: CD-B

相对于CD-A的强约束(无条件约束),它的约束更灵活一些,是有条件的约束。

v2-d7485b13ac76117de9242942f85104bf_b.png

观察上图的变形,我们可以看到无效的形式是这样的:原来e1,e2都在Ob下,现在e2在Ob下,e1不在Ob下了,所以更精确的约束是:

不允许出现,当e2在Ob下时(前提条件),e1却不在Ob下(冲突)!

这种有前提条件的约束,用conflict rules来描述:

image.png

也就是 : 如果T1集合有表在(image.png)下,则T2集合必须完全在S下!

这样就不用扩展TES了,使其与SES一致即可,只要额外保证这些CR(conflict rules)不被违反就行了。收集CR的算法如下,总体流程类似CD-A收集TES:

image.png

同时检查条件就变为了:

image.png

这里仍然有L-TES/R-TES,但TES已经和SES等价了,没有更广泛的含义。同时CR(o)的所有rules都要被满足,否则就认为发现冲突。

这个算法仍然只能保证正确性,而不是完备性,例如如下例子:

v2-54e759868e86ade44c6a0641fcfd682b_b.png

观察左侧的operator tree,由于r-asscom(inner-join0,1, semi-join1,3) 不能成立,会建立如下的CR:

image.png

因为在考察inner-join0,1和semi-join 1,3的r-asscom时,会把(R1 inner join R2)看做一个整体,r-asscom并不合法(冲突表),(R1 inner join R2)这个整体被有条件的限制在了O0,1的下面,导致右侧的有效plan tree无法生成。

但其实inner-join0,1和semi-join 1,3都不引用表R2,所以可以对R2不做限制,从而更精细的控制T2所涉及的表集合为ob真正引用的表!这里由原来的R3 -> (R1, R2) 变为R3 -> R1。

Step 3: CD-C

针对上面的问题,CD-C是更细粒度的控制,其他方面都和CD-B完全相同,只是对T2的处理上更细,只考虑ob真正引用到的表集合。这个算法是可以生成所有有效的plan的,但paper中没有给出完备性证明。

image.png

从line 5可以看到,在推导T2时,使用了 image.png,也就是只约束算子oa真正引入的表集合。

总结

经过上面复杂的算法描述后,已经给出了具有完备+正确的冲突检测机制,也就是我们所需要的APPLICABLE函数,把它与任意的graph-based DP算法结合,就能保证完整的枚举所有可能的join组合,选取最优计划。

目录
相关文章
“Could not find suitable distribution for Requirement.parse(‘XXXX‘)”的问题
“Could not find suitable distribution for Requirement.parse(‘XXXX‘)”的问题
352 0
Invalid mapping pattern detected: /download/{{fileName}} ^Not allowed to nest variable c
Invalid mapping pattern detected: /download/{{fileName}} ^Not allowed to nest variable c
|
SQL
Parameter ‘id‘ not found. Available parameters are [collection, list]
Parameter ‘id‘ not found. Available parameters are [collection, list]
235 0
|
11月前
DeprecationWarning:current URL string parser is deprecated, and will be removed in a future version.
DeprecationWarning:current URL string parser is deprecated, and will be removed in a future version.
|
数据库
解决numpy.core._exceptions.UFuncTypeError: ufunc ‘add‘ did not contain a loop with signature matching
解决numpy.core._exceptions.UFuncTypeError: ufunc ‘add‘ did not contain a loop with signature matching
1113 0
解决numpy.core._exceptions.UFuncTypeError: ufunc ‘add‘ did not contain a loop with signature matching
|
前端开发 5G
Search space set group switching(一)
根据R17 38.300的描述,UE可以通过PDCCH monitoring adaptation机制实现power saving的目的,这其中就包括PDCCH monitoring skipping和search space set group (SSSG) switching两种机制。PDCCH monitoring skipping是R17才提出的机制,就是UE 可以在PDCCH skipping的时间内不监视 PDCCH的功能;search space set group (SSSG) switching R16提出,R17进行了部分增强。
Optional int parameter ‘id‘ is present but cannot be translated into a null value due to being ……
Optional int parameter ‘id‘ is present but cannot be translated into a null value due to being ……
304 0
|
NoSQL MongoDB 数据库
DeprecationWarning: count is deprecated. Use Collection.count_documents instead
当我使用pymongo查询出对应的cursor(find出的document的迭代器),然后查看查询出数据的数量时使用如下代码: ```python db = MongoClient(host='192.168.1.3', port=27017) # dbname为操作的数据库名称,collectionname为操作的集合名称
345 0
【问题记录】utureWarning: Function get_feature_names is deprecated; get_feature_names is deprecated in 1.0
【问题记录】utureWarning: Function get_feature_names is deprecated; get_feature_names is deprecated in 1.0
|
Kotlin
Program type already present: org.intellij.lang.annotations.Flow\Program type already present: org.i
Program type already present: org.intellij.lang.annotations.Flow\Program type already present: org.i
125 0
Program type already present: org.intellij.lang.annotations.Flow\Program type already present: org.i