代数优化是查询优化的关键方面之一,它涉及关系代数表达式的变换和改进。 这些表达式通常由查询处理过程中的查询分析和检查步骤生成。 通过应用一系列代数规则和启发式方法,代数优化的目标是找到成本最低的执行计划。
以下是关系代数表达式变换的一些基本规则,这些规则是代数优化过程中常用的工具:
交换:连接和笛卡尔乘积运算都充满了**反转,即改变运算顺序不影响结果。
笛卡尔乘积交换定律:$e 1 乘以 e2 = e2 乘以 e1 $$
自然联系的交换定律:$e 1 领结 e2 = e2 领结 e1 $$
自然联系的交换定律:$e 1 bowtief e2 = e2 bowtief e1$$
关联性:连接和笛卡尔积也满足并集定律,这使我们能够在不改变结果的情况下重新组织运算的组合方式。
笛卡尔乘积关联性:$e 1 乘以 e2) 乘以 e3 = e1 乘以 (e2 乘以 e3) $
自然连接粘接法则:$e 1 个领结 e2) 领结 e3 = e1 领结 (e2 领结 e3) $
条件连接关联定律: $e 1 bowtie1} e2) bowtie2} e3 = e1 bowtie1} (e2 bowtie2} e3) $
选择级联选择定律:选择操作可以串联执行,也可以按任何顺序执行。 合并条件(和连接条件)可以分解为多个选择操作。
选择运算顺序的换向定律:$sigma( sigma(e)) = sigma( sigma(e))。
连词条件分解定律:$sigma(e) = sigma(e) cap sigma(e) $
投影运算串联定律:如果投影的属性集是另一个投影属性集的子集,则可以将两个投影操作合并为一个。
pi( pi(e)) = pi(e) 当 (ai subseteq b j)$$
选择换向与其他操作:在某些情况下,选择操作可以与笛卡尔积、投影和连接操作进行交换,以便可以先过滤小数据集,从而减少后续操作的数据量。
选择和笛卡尔积:$sigma(e1 times e2) = ( sigma(e1)) times e2 $$,如果 (f) 中涉及的所有属性都是 ($e 1$) 中的所有属性。
选择和投影:$sigma( pi(e)) = pi( sigma(e)) 如果 (f) 仅与 (a) 的性质有关。
分配性:对于并集和自然连接操作,选择操作是分布式的,而投影操作对于笛卡尔乘积求和操作是分布式的。
选择和平行: $sigma(e1 cup e2) = sigma(e1) cup sigma(e2) $
选择和差分算法: $sigma(e1 - e2) = sigma(e1) -sigma(e2) $
选择与自然联系:$sigma(e1 领结 e2) = ( sigma(e1)) 领结 ( sigma(e 2)) 如果 (f) 只涉及共同属性。
这些转换规则的应用使我们能够将复杂的查询表达式转换为一系列更有效的操作步骤。 例如,通过推迟对大型表的联接操作,并首先对小型表执行选择和投影操作,可以显著降低查询成本。
代数优化的目标
代数优化的最终目标是减少查询期间的磁盘操作,因为磁盘访问通常是数据库操作中最耗时的部分。 此外,优化器还尝试减小中间结果的大小,并降低 CPU 计算的复杂性。
在实际的DBMS中,代数优化通常是自动化的。 优化器根据数据库统计信息(如表的大小、索引的存在与否、数据的分布特征)选择最佳执行计划。 但是,数据库管理员和有经验的用户可以通过查询重写或使用特定的查询提示来帮助或指导优化程序的选择。
总的来说,代数优化是查询优化的一个组成部分,确保数据库系统能够以最有效的方式执行用户的查询请求。
选择操作优先原则:此规则的基础是,尽早过滤掉不相关的数据可以显著减少后续操作处理的数据量。 通过在查询的早期应用选择(筛选)条件,可以避免对不需要的数据执行复杂的操作。
投影操作优先原则:与选择操作类似,投影(确定需要哪些列)操作也应尽早进行,以减小处理数据的宽度,尤其是在联接操作之前。
笛卡尔积合并规则:笛卡尔乘积产生大量中间结果,通常是不必要的。 因此,笛卡尔积应尽可能与选择和投影等其他操作相结合,以减少中间结果的产生。
提取常用表达式规则:如果一个计算在多个地方重复,并且计算的结果集很小,则应计算一次,并将结果缓存起来以供后续操作使用。
假设我们想从数据库中检索所有选修课,其数字为'02'课程的学生姓名。 SQL 查询语句可能如下所示:
select s.sname from s, sc where s.sno = sc.sno and sc.cno = '02';在没有优化的情况下,数据库可能会在应用选择条件之前执行笛卡尔积,这是非常低效的。 应用启发式方法后,我们可以按如下方式细化查询执行计划:
选择操作优先级首先,我们应用选择标准sc.cno='02'
筛选sc
表,用于减小后续操作的数据集大小。
投影操作优先: 其次,我们只选择s.sname
跟s.sno
,以及符合条件的人sc.sno
以进一步减小数据的宽度。
合并笛卡尔乘积:我们在选择操作后立即进行连接操作,避免了不必要的大量中间数据的生成。
提取常用表达式:如果sc.cno='02'
是一种常见的查询条件,其结果可以缓存,以便后续查询快速使用。
优化后的查询执行方案如下:
select s.sname from s join sc on s.sno = sc.sno where sc.cno = '02';在这个优化的查询计划中,我们从
sc
该表应用选择条件,然后仅对此筛选的结果集执行联接操作。 这种策略将大大提高查询的效率。
从这些示例中,我们可以看到适当的查询优化可以极大地提高数据库系统的性能。 此过程通常是自动化的,用户不必担心底层执行细节,但了解这些优化原则可以帮助您编写更高效的查询。
物理优化是查询优化的重要组成部分,它涉及底层数据访问方式和执行算法的选择。 目的是通过选择最有效的访问路径和操作算法减少资源消耗,提高查询执行过程中的查询速度。 物理优化通常包括基于启发式规则的优化和基于成本估算的优化。
在物理优化中,启发式规则提供了一组经验法则,可以在没有详细统计信息的情况下快速指导访问路径的选择。
选择操作的启发式规则
对于具有小关系的查询,即使存在索引,全表扫描也往往是最快的。
对于主键等效查询,通常使用主键索引,因为结果最多只有一个元组。
对于非主要属性的等效查询,如果存在索引并且预期返回的元组数量较少(小于总数的 10%),则应考虑索引扫描; 否则,使用全表扫描可能更有效。
对于不相等或范围查询,如果结果集较小且具有索引,则同样应优先考虑索引扫描。
对于具有 AND 联接的多条件查询,如果存在涉及多个条件的复合索引,则复合索引优先。
对于已联接 OR 的查询,通常使用完整表扫描。
连接操作的启发式规则
如果两个表都已按联接属性排序,则应使用排序合并联接。
如果其中一个表在 join 属性上具有索引,则通常首选索引联接。
如果两个表都很大并且没有索引优势,请考虑使用哈希联接。
如果上述规则均不适用,请考虑使用嵌套循环联接,尤其是当一个表比另一个表小得多时。
假设我们有一个简单的查询:从employees
在表中检索部门为“销售”的所有员工记录。 我们的数据库包含以下统计数据:
employees
共享的表b
块。
每个块可以包含r
记录。
部门列具有非聚集索引。
基于成本的优化计算每个操作算法的执行成本,这需要了解数据库的状态,包括:
表的元组总数 (n)。
表的元组的平均长度 (l)。
表(b)占用的数据块数。
每个字段的不同值数 (m)。
字段选择率 (s)。
索引的层数 (l)。
索引的选择基数 (s)。
基于上述信息,查询优化器可以使用以下算法来估算执行查询的成本:
全表扫描算法成本估算
成本 ( 成本 = b )。
如果选择条件为“主代码 = 值”,则平均搜索成本 (cost = frac$)。
估算索引扫描算法的成本
如果选择条件为“code = value”,则使用表的主索引,如果是b+树,层数为l,则成本(成本=l+1)。
嵌套环路连接算法成本的估算
对于两个表的嵌套循环联接,如果其中一个表较小,则可以将较小的表放入内部循环中,代价为 ( cost = b + frac times b }$
排序-合并连接算法成本估算
如果联接表已按联接属性排序,则成本 ( 成本 = b + b + 压裂乘以 n 乘以 n}}$
其中:$b$) 和 ($b$) 分别是两个连接表占用的数据块数。
k ) 是内存中可用于存储数据的数据块的数量。
f$ 是连接选择性,表示连接结果的元组数占比例。
m $ 是保存连接结果的块因子,表示每个块中可以存储的结果元组的数量。
假设employees
该表有 1000 个数据块,部门列的非聚类索引有 3 个图层,我们希望使用此索引快速定位“销售”部门中的员工。
估算完整表扫描的成本
成本 ( 成本 = 1000$ )。
估算索引扫描的成本
成本 (成本 = 3 + 1 = 4$) 假定索引扫描直接以所需记录为目标)。
如果“销售”部门的员工百分比低于 10%,则索引扫描将是一种更有效的方法,因此我们选择它作为我们的查询策略。 在更复杂的查询中,需要考虑更多因素,例如联接操作、并行执行等。