Fundamental Techniques for Order Optimization

这是一篇1996年的老paper了,主要讲解了IBM DB2如何针对query当中的有序性进行优化。但对于后续physical property的优化有较为深远的影响,由于DB2的优化器起源于System-R以及其后续演进的starburst,因此延续了system-R中的interesting order和order property的概念。关于system-R的介绍请看之前的文章

order这种physical property并不只限于order by算子,基于有序的group by/distinct等,都会利用到数据的排序操作,而排序本身就是比较昂贵的计算,因此应该对其做尽可能的优化。

这篇paper系统化的提出了对于有序性进行优化的框架,并描述了一些DB2内部相关的工程实现。

DB2优化器

DB2 optimizer是基于starburst的后继,其思路与system-R一脉相承,优化过程分为了若干个阶段:

  1. query进入parser解析后,会生成QGM (query graph model),也就是AST,用来描述逻辑关系运算,之后应有一系列启发式规则做查询变换,生成等价但更为高效的QGM。
  2. 基于生成的QGM,做cost based优化,生成物理执行plan即QEP(query execution plan)。

Fundamental Techniques for Order Optimization

右侧是物理plan,可以看到物理算子间的箭头表示data stream,每个data stream会具有若干的属性(property)。

每个output stream的property,是由其对应算子的物理操作特性,和输入stream的property决定的。

在开始做CBO之前,优化器会top-down的遍历QGM,针对不同算子可能的有序性需求(join/group by/distinct/order by),建立算子的interesting order,这个过程称为对QGM的order scan

在order san的过程中所生成的interesting order,会尽可能下推到下层算子中(sort-ahead),以尽早满足order属性要求。此外如果一个算子具有多个interesting order,会尝试将他们合并,这样一个排序就可以满足多个order属性的需求。

在优化过程中,DB2采用了类似system-R的bottom-up物理优化,在选择每个算子的物理算法时,也会产生对应的property,每个算子都会产生多种可能的输出属性,并基于等价属性做pruning。在这期间,物理算子的interesting order会被考虑并与下层算子的output order property进行测试(判断其输出是否可以满足interesting order的要求)如果无法满足则加入sorting操作,并相应增大代价。

Order优化算法

本质上,order的优化有3个目标:

  • sorting的消除

如果data stream的order property能够满足interesting order,则不需要加sort操作

  • 最小化排序的列

同时对order property和interesting order都要做reduce,将其尽量化简为最简形式

  • order的下推,尝试提前做掉

为了能够尝试sort-ahead,会在初始阶段,将interesting order尽量尝试下推

相关的操作及对应算法有如下3个

Reduce Order

比如一个order {c1,c2..cn},如果应用过谓词c1=5,则order就可以化简掉c1变成{c2..cn}

如果应用过 c2=b 这样的等值谓词,可以归一化为 {b .. cn}

如果c1是主键(key),则利用functional dependency(FD),直接去掉了c2...cn,变为{c1}

以上这些,本质上都是利用FD,无论是key还是const值,都是FD的特殊形式而已。

算法:

Fundamental Techniques for Order Optimization

给定一个Order的描述,希望化简为统一的最简形式,算法其实很简单

  1. 根据应用等值谓词,用等价列中的head,替换对应列(所谓等价列就是具有等值关系的列集合,类似于MySQL中MEP的head)
  2. 对于{c1 ... cn}的规格,从后往前遍历,如果Cj,可以被C1...Cj-1唯一决定(具有FD),则Cj是冗余的,消掉

Test Order

测试一个算子的output order property(OP)是否满足interesting order需求,算法非常简单

Fundamental Techniques for Order Optimization

只要interesting order的排序列是output OP 排序列的前缀即可,注意interesting order和output OP都要先做reduce来达成统一形式,且最小化比较的列数。

Cover Order

前面已经提到,在做top-down order scan时,会尝试将2个interesting order合并。

Fundamental Techniques for Order Optimization

注意仍然要先做reduce来形成统一描述,再考虑是否一个interesting order是另一个的前缀。

Homogenize Order

也就是将前面提到的,将interesting order尽量下推,使sort ahead成为可能。

为了能够下推到比如join的某一个表,目标是将{c1...cn}这样的规格,可以描述为{t1.a, t1.b..}这样的列形式,因此也需要类似reduce的过程,但不一样的是,不一定要用已应用谓词做替换,因为这里只是要求在bottom->up的过程中,逐渐应用谓词后可以满足interesting order,因此可以用到目标算子(提出interesting order的算子)前的所有谓词来尝试reduce。

Fundamental Techniques for Order Optimization

例如查询

select * from a, b where a.x = b.x order by a.x, b.y;

初始的interesting order I = (a.x, b.y),如果想把I推导b表的scan上,则需要利用a.x = b.x,转换为 (b.x, b.y)的形式。

注意上述算法仍然要先对I做reduce,如果上例中有FD : a.x是a表的key且join后仍是join result的key,则有 {a.x} -> {b.y},整个I被reduce为 (a.x),可以下推到表a,而不是表b。 

DB2的order优化框架

有了以上的基础算法后,我们看下DB2的一些实现细节

Order Scan

这个操作发生在实际的优化阶段之前,相当于做一些优化前的预处理。相对于上面的理论,实现中增加了order requirement这个概念,与interesting order不同,order requirement是必须要满足的order属性要求,例如每个算子的输出必须如何有序,算子的输入必须具有什么样的顺序。

例如order by的输出一定要求有序,而group by的输入也要求一定有序(在使用流式group by的前提下)。论文中没有详细说明order requirement和interesting order有何区别,我的理解是,到算子的实际执行处就是requirement,而由于有可能做sort-ahead,但不强制做,因此在远离实际算子时用interesting order描述。

概念上order scan分为4个阶段:

  1. 决定每个算子的input / output order requirement
  2. 决定distinct算子的interesting order
  3. 决定merge join/subquery的interesting order
  4. top-down遍历QGM,尝试pushdown interesting order,并做homogenize order + cover order

这里有个问题,由于order scan是在优化之前,其实是无法确定到达一个算子时,所应用的谓词以及FD的(这些都是优化过程中产生的物理属性信息)。问题的解决方法是乐观假设,认为一个算子下原始的所有谓词都已应用,如果interesting order无法homogenize到某个特定的input stream,则将I转换为可以最大化homogenize的前缀,然后在优化阶段,检测这些假设是否依然成立(不成立的interesting order消除掉)。

基于property的优化

CBO阶段,采用bottom-up依次处理每个逻辑算子。算子内做各种可能形式的枚举,产生不同的数据流和对应的不同属性,最终基于代价选出最优的,对于每个算子,也有各个候选plan的合并+剪枝操作(参考system-R )。这时,data stream的properties就起到了作用。

剪枝的基本思路就是,对于任意两个subplan1、subplan2,如果1的代价更小,且在属性上更”通用“(用Fundamental Techniques for Order Optimization 表示),则2可以被剪枝掉。所以针对每种property,都有一个比较谁更通用的描述。

针对order的优化这个问题,主要涉及如下一些属性

  • order property: 描述了output stream的order属性
  • predicate property: 积累从算子树的最底层到当前,已应用谓词集合
  • key property: data stream中unique keys的集合
  • FD property: data stream中functional dependency的集合

针对每种属性,都有相同的几个问题需要考虑

  • 属性从哪产生?
  • 属性如何跨算子传播,在哪终止?
  • 属性间如何比较?

下面依次看下

Order Property

  1. 只能由ordered index scan / sorting 产生
  2. 对于一个OP{c1...cn},如果投影去掉了其中某列cj,则只有{c1...cj-1}这个前缀仍保持有序性。对于join操作,如果是NL/SMJ,则外表的有序性可以传播,如果是HS,有序性都不可以传播(可能有spill操作)
  3. 两个OP,通过Test Order算法就可以比较。完成reduce后,如果OP1是OP2的前缀,则OP2比OP1更通用

Predicate Property

  1. 只要一个算子apply了某个谓词,这个谓词就加入到data stream的已应用谓词集合中
  2. 这个没有终止一说
  3. 如果一个谓词集合,是另一个的超集,则更大的一方,更具有通用性

Key Property

KP可能包含有多个unique keys,是一个集合的概念,本质上,Key只是FD的一个特例。

  1. base table自带的基本约束,此外通过group by/distinct之后也可以产生
  2. 如果有投影去掉了key中的某列,key的唯一性约束不再保证;如果join是n:1的,则左表的唯一性可以传播,反之如果是1:n的,右表的唯一性可以传播,否则唯一性终止;但join之后,可以产生新的唯一key: left_key+right_key 连接起来,形成新的唯一key
  3. 对于K1, K2,各自进行reduce后,如果K1是K2的子集,则说明用更少的列,就可以唯一决定一行,那么对于KP2中的每个K2,都存在KP1中的K1,能够决定K2,则KP1整体更具有通用性

FD Property

FP是FD的集合,可能会包含下层传递上来的多个FD。

  1. 当key无法传播过join时,就退化形成FD  {c1} -> {c2..cn} (它仍然可以决定属于自己表的部分列)。因此在join时左右两边data stream的key property合并成了一个,同时那些无法传播的Key Property,加入到FD集合中
  2. 当遇到投影时,对于FD: A -> B,如果减掉A中的列,则FD不再成立,如果减掉B中的列,则FD仍然成立
  3. 对于FD1 A1 -> B1, FD2  A2 -> B2。如果A1是A2的子集,B1却是B2的超集,则FD1 可以推导出FD2,FD1更具有通用性;如果FP2中每个FD2,都可以被FP1中某个FD1,推导出来,则FP1整体,更具有通用性

一个栗子

paper中给出了一个便于理解的示例

select a.x, a.y, b.y, sum(c.z)
from a, b, c
where a.x = b.x and b.x = c.x
group by a.x, a.y, b.y
order by a.x;

Fundamental Techniques for Order Optimization

这个例子中,order by的interesting order (a.x) 会在早期下推并与group by的interesting order做cover,形成(a.x, a.y, b.y)。然后再进一步下推到join,与merge join的interesting order做homogenize,建立(b.x)。

由b.x这个key形成的FD : {b.x} -> {b.y},以及join形成的FD : {a.x} -> {b.x},可以用来reduce group by的interesting order,变为{a.x, a.y}。因此在table a上基于(a.x, a.y)做排序就可以满足所有顺序属性。当table a的数据量较小时,很可能这是一个最优的计划。

总结

本paper提出的思想并不复杂但却十分有用,影响深远。尤其是这种基于functional dependency做reduce的思想,后续又扩展到了除了order外的其他属性中。另外提一句,在PolarDB的并行优化中,也借鉴了一些这种思路,不过由于场景不同,实现有所区别。

上一篇:IOS设计模式-备忘录模式


下一篇:万字熟通Spring框架