KingbaseES查询逻辑优化深度解析:从子查询到语义优化的全链路实践

目录

    • 引言
    • 一、子查询优化
      • [1.1 子查询提升](#1.1 子查询提升)
      • [1.2 EXISTS子连接提升](#1.2 EXISTS子连接提升)
      • [1.3 ANY子连接提升](#1.3 ANY子连接提升)
      • [1.4 合并子查询公共表达式](#1.4 合并子查询公共表达式)
      • [1.5 合并 EXISTS子连接](#1.5 合并 EXISTS子连接)
    • 二、等价谓词重写
      • [2.1 扩展EXISTS子连接](#2.1 扩展EXISTS子连接)
      • [2.2 不等式变换](#2.2 不等式变换)
    • 三、连接优化
      • [3.1 外连接消除](#3.1 外连接消除)
      • [3.2 消除无用表项](#3.2 消除无用表项)
      • [3.3 半连接转换](#3.3 半连接转换)
    • 四、算子移动
      • [4.1 谓词下推](#4.1 谓词下推)
      • [4.2 UNION外层条件下推](#4.2 UNION外层条件下推)
    • 五、语义查询优化
      • [5.1 COUNT(DISTINCT)查询优化](#5.1 COUNT(DISTINCT)查询优化)
      • [5.2 distinct消除](#5.2 distinct消除)
      • [5.3 消除无用的目标列](#5.3 消除无用的目标列)
      • [5.4 分组键值消除](#5.4 分组键值消除)
    • 结语

引言

KingbaseES数据库优化器在生成具体的执行计划之前,会基于关系代数理论,对输入的SQL语句进行一系列等价变换处理,将原始查询转换为更高效的执行方式。

KingbaseES优化器支持的逻辑优化规则可分为两大类:其中,能够确保变换后性能一定有所提升的规则被称为启发式规则;而另一类规则则需要优化器根据代价估算自动决定是否采用,这类规则称为基于代价的规则。 参数kdb_rbo.rbo_rule用于控制基于代价的逻辑优化规则的启停状态,默认设置为off表示该功能处于关闭状态。用户可根据实际需求选择:强制使用时将参数值设为on;需要优化器根据代价估算自动选择最优策略时则设为cost。

一、子查询优化

1.1 子查询提升

优化器支持将FROM子句中的子查询上移至父查询,使其与父查询直接进行连接操作。这种子查询提升特性为启发式规则,通过提供更多的连接顺序和方式选择,使优化器能够生成更优的执行计划。

下面的例子将表t1与子查询v的连接转换为表t1与表t2的连接。

sql 复制代码
EXPLAIN (costs off) SELECT * FROM t1, (SELECT t2.b FROM t2) v WHERE t1.a = v.b AND t1.a < 10;
           QUERY PLAN
--------------------------------
 Merge Join
   Merge Cond: (t1.a = t2.b)
   ->  Sort
         Sort Key: t1.a
         ->  Seq Scan on t1
               Filter: (a < 10)
   ->  Sort
         Sort Key: t2.b
         ->  Seq Scan on t2
(9 行记录)

1.2 EXISTS子连接提升

若EXISTS子连接中包含父查询表的列,则该子连接被称为相关子连接。KingbaseES优化器针对此类结构提供特定优化:将相关EXISTS子连接提升为半连接操作,将相关NOT EXISTS子连接优化为反连接操作;而对于不相关的EXISTS子连接则保持原有结构不变。此外,当连接条件为等值匹配时,优化器会为子连接增加去重操作,并将其转换为内连接操作。

sql 复制代码
EXPLAIN (costs off) SELECT * FROM t1 WHERE EXISTS(SELECT 1 FROM t2 WHERE t1.a > t2.a);
          QUERY PLAN
------------------------------
 Nested Loop Semi Join
   Join Filter: (t1.a > t2.a)
   ->  Seq Scan on t1
   ->  Materialize
         ->  Seq Scan on t2

1.3 ANY子连接提升

使用IN、SOME或ANY关键字的子连接统称为ANY子连接。当这些子连接中不涉及父查询变量时,优化器会自动将基础的ANY子连接转换为半连接操作,将NOT ANY子连接转换为反连接操作。这种转换策略属于启发式规则,通过将子连接转换为特定的连接类型,使得子连接中的表只需进行一次扫描。这种方式不仅提高了执行效率,还为优化器提供了更多的优化选择空间。

示例1:将ANY子连接变成t1,t2表的半连接。

sql 复制代码
EXPLAIN (costs off) SELECT * FROM t1 WHERE a >= ANY(SELECT a FROM t2);
          QUERY PLAN
-------------------------------
 Nested Loop Semi Join
   Join Filter: (t1.a >= t2.a)
   ->  Seq Scan on t1
   ->  Materialize
         ->  Seq Scan on t2

示例2:将NOT ANY子连接变成t1,t2表的反连接

sql 复制代码
EXPLAIN (costs off) SELECT * FROM t1 WHERE a NOT IN(SELECT a FROM t2);
         QUERY PLAN
----------------------------
 Hash Anti NA Join
   Hash Cond: (t1.a = t2.a)
   ->  Seq Scan on t1
   ->  Hash
         ->  Seq Scan on t2

1.4 合并子查询公共表达式

对查询语句中各子查询内的公共表达式进行识别并实施合并策略,以减少重复的表访问和连接操作次数,从而提高整体查询性能。

该特性缺省为关闭状态,使用时需要设置以下参数:

sql 复制代码
-- 方法1:在数据库中配置
SET kdb_rbo.rbo_rule = on;
SET kdb_rbo.enable_merge_comm_expr = on;
-- 方法2:在kingbase.conf文件中配置
kdb_rbo.rbo_rule = on
kdb_rbo.enable_merge_comm_expr = on

该变换为基于代价的逻辑优化规则,支持两种使用场景:

场景一:SQL中存在重复的子查询以及多个子查询中存在同样的表达式,可以将这几个子查询合并。例如下面的例子,SELECT a FROM t2 WHERE t2.b > 10和SELECT b FROM t2 WHERE t2.b > 10在执行计划中被合并成了一个公共的查询SELECT a,b FROM t2 WHERE t2.b > 10,从而对表t2的扫描次数减少为一次。相似的子查询在SQL中出现的次数越多并且合并后查询出的结果集越小,则该规则对SQL的性能提升越好。

sql 复制代码
EXPLAIN (costs off) SELECT * FROM (SELECT a FROM t2 WHERE t2.b > 10) v1,  (SELECT b FROM t2 WHERE t2.b > 10) v2 WHERE v1.a = v2.b;
                        QUERY PLAN
-----------------------------------------------------------
 Hash Join
   Hash Cond: (generated_cte_1.a = generated_cte_1_1.b)
   CTE generated_cte_1
     ->  Seq Scan on t2
           Filter: (b > 10)
   ->  CTE Scan on generated_cte_1
   ->  Hash
         ->  CTE Scan on generated_cte_1 generated_cte_1_1

场景二:多个UNION ALL分支中存在共同访问的表,且公共表涉及到的连接字段和过滤条件相同,则可以提取这些公共表和原来的UNION ALL进行连接。

sql 复制代码
EXPLAIN (costs off) 
SELECT t1.a,t2.b FROM t1,t2 WHERE t1.a = t2.b AND t2.b = 12 
union all 
SELECT t1.a,t2.a FROM t1,t2 WHERE t1.a = t2.b;
           QUERY PLAN
---------------------------------------
 Merge Join
   Merge Cond: (t1.a = t2_1.b)
   ->  Sort
         Sort Key: t1.a
         ->  Seq Scan on t1
   ->  Sort
         Sort Key: t2_1.b
         ->  Append
               ->  Seq Scan on t2 t2_1
                     Filter: (b = 12)
               ->  Seq Scan on t2
(11 行记录)

1.5 合并 EXISTS子连接

当SQL语句中包含多个(NOT)EXISTS子连接且这些子连接之间存在"包含"或"被包含"的关系时,优化器将通过转换生成新的单一(NOT)EXISTS子连接或布尔表达式,从而减少重复执行子连接的次数。、

这一转换策略属于基于代价的逻辑优化规则,默认处于关闭状态,需要配置以下参数以启用该功能:

sql 复制代码
SET kdb_rbo.rbo_rule = on;
SET kdb_rbo.enable_coalesce_exists_sublink = on;

例如,

子连接S1,S2相同

WHERE EXISTS(S1) AND/OR EXISTS(S2) 可以转换为WHERE EXISTS(S1)。

WHERE EXISTS(S1) AND NOT EXISTS(S2)可以转换为WHERE FALSE。

WHERE EXISTS(S1) OR NOT EXISTS(S2) 可以转换为WHERE TRUE。

子连接S1包含S2。

WHERE EXISTS(S1) AND EXISTS(S2) 可以转换为WHERE EXISTS(S2)。

WHERE EXISTS(S1) OR EXISTS(S2) 可以转换为WHERE EXISTS(S1)。

子连接S1, S2处于不同析取合取层的合并。例如下面的例子S1,S2相同时,

WHERE (A AND EXISTS(S1)) OR EXISTS(S2)可以转换为 WHERE EXISTS(S2)

sql 复制代码
EXPLAIN (costs off) SELECT * FROM t1 WHERE 
(t1.a > 10 AND EXISTS(SELECT 1 FROM t2 WHERE a < 10 AND t2.a < t1.a)) 
OR  
EXISTS(SELECT 1 FROM t2 WHERE a < 10 AND t2.a < t1.a);
           QUERY PLAN
--------------------------------
 Nested Loop Semi Join
   Join Filter: (t1.a > t2.a)
   ->  Seq Scan on t1
   ->  Materialize
         ->  Seq Scan on t2
               Filter: (a < 10)

二、等价谓词重写

2.1 扩展EXISTS子连接

针对SELECT子句中的EXISTS子连接,若其WHERE Clause的顶层判断条件为OR关系,并且其中部分OR条件未涉及子连接所访问表的列,则可以将这部分条件扩展为新的独立子连接,并与原子连接形成逻辑或的关系。通过这种优化方式,能够有效利用索引机制,缩短SQL语句的执行时间。

该转换规则属于基于代价的逻辑优化策略,默认处于关闭状态。在实际应用中需要配置相关参数以启用此功能:

sql 复制代码
SET kdb_rbo.rbo_rule = on;
SET kdb_rbo.enable_exists_expand = on;

例如,EXISTS子连接中存在外层查询的变量t1.b <10,子连接可以等价转换为exists (SELECT 1 FROM t2 WHERE t2.a = 1) OR EXISTS (SELECT 1 FROM t2 WHERE t1.b <10)

sql 复制代码
EXPLAIN (costs false) SELECT * FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.a = 1 OR t1.b <10);
               QUERY PLAN
----------------------------------------
 Seq Scan on t1
   Filter: ($0 OR (SubPlan 2))
   InitPlan 1 (returns $0)
     ->  Seq Scan on t2
           Filter: (a = 1)
   SubPlan 2
     ->  Result
           One-Time Filter: (t1.b < 10)
           ->  Seq Scan on t2 t2_1

2.2 不等式变换

对于满足特定转换规则的多个不等式约束条件,优化器将实施等价变换处理。这种转换能够有效消除冗余过滤条件,并更好地利用索引机制,从而显著提升查询性能。

对于多个不等式约束条件进行析取和合取操作,优化器可以进行等价变换处理。经过转换后的新SQL语句能够有效消除冗余过滤条件,并更好地利用索引机制,从而实现性能提升的目标。

示例1:and条件保留取值范围小的条件

sql 复制代码
EXPLAIN (costs off) SELECT * FROM t1 WHERE t1.a < 156 AND t1.a < 10;
     QUERY PLAN
--------------------
 Seq Scan on t1
   Filter: (a < 10)

示例2:or条件保留取值范围大的条件

sql 复制代码
EXPLAIN (costs off) SELECT * FROM t1 WHERE t1.a < 156 OR t1.a < 10;
     QUERY PLAN
---------------------
 Seq Scan on t1
   Filter: (a < 156)

示例3:条件冲突时生成false

sql 复制代码
EXPLAIN (costs off) SELECT * FROM t1 WHERE t1.a < 10 AND a > 10;
        QUERY PLAN
--------------------------
 Result
   One-Time Filter: false

三、连接优化

3.1 外连接消除

在满足特定条件的情况下,KingbaseES优化器能够自动将外连接转换为一种执行效率更高的连接形式。

其基本原理如下:由于外连接会对不满足连接条件的记录补全NULL值,因此当查询结果中可以确保完全不存在需要补全NULL的情况时,外连接(包括左连接、右连接和全连接)均可转换为内连接以提高性能。若仅需保证某一侧记录无需补全,则可将全连接转换为左连接。

示例1:条件t2.b is not null保证左连接的右侧不存在值为NULL的记录,则左连接可以转换内连接

sql 复制代码
EXPLAIN (costs off) SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t2.b IS NOT NULL;
              QUERY PLAN
---------------------------------------
 Merge Join
   Merge Cond: (t2.a = t1.a)
   ->  Sort
         Sort Key: t2.a
         ->  Seq Scan on t2
               Filter: (b IS NOT NULL)
   ->  Sort
         Sort Key: t1.a
         ->  Seq Scan on t1
(9 行记录)

示例2:条件t2.b is not null只能保证全连接的右侧不存在值为NULL的记录,则全连接转换右连接

sql 复制代码
EXPLAIN (costs off) SELECT * FROM t1 FULL JOIN t2 ON t1.a = t2.a WHERE t2.b IS NOT NULL;
              QUERY PLAN
---------------------------------------
 Merge Left Join
   Merge Cond: (t2.a = t1.a)
   ->  Sort
         Sort Key: t2.a
         ->  Seq Scan on t2
               Filter: (b IS NOT NULL)
   ->  Sort
         Sort Key: t1.a
         ->  Seq Scan on t1
(9 行记录)

示例3:条件t2.a is null保证连接的右侧全部为补NULL的记录,与反连接的要求相似,所以左连接可以转换为反连接

sql 复制代码
EXPLAIN (costs off) SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t2.a IS NULL;
         QUERY PLAN
----------------------------
 Hash Anti Join
   Hash Cond: (t1.a = t2.a)
   ->  Seq Scan on t1
   ->  Hash
         ->  Seq Scan on t2

3.2 消除无用表项

在等值连接条件下,若参与连接的表具有唯一约束,则可以考虑消除部分表而不会影响最终的连接结果。这一优化策略属于启发式规则。

KingbaseES优化器对于左连接和右连接,在满足以下条件时可消除内表: (1) 连接条件中内标的列有唯一性质, 如:唯一索引、distinct的列等 (2) 内表的相关内容仅出现在连接条件后

sql 复制代码
EXPLAIN (costs off) SELECT t1.* FROM t1 LEFT JOIN (SELECT DISTINCT a FROM t2) v ON t1.a  = v.a;
   QUERY PLAN
----------------
 Seq Scan on t1

3.3 半连接转换

当连接条件满足唯一性质时,优化器可以将半连接成功转换为内连接操作。 例如,若IN子查询中包含DISTINCT关键字,则能够保证结果的唯一性,此时最终的执行计划将被优化为内连接方式

sql 复制代码
EXPLAIN (costs off) SELECT * FROM t1 WHERE a IN (SELECT DISTINCT a FROM t2);
            QUERY PLAN
----------------------------------
 Hash Join
   Hash Cond: (t1.a = t2.a)
   ->  Seq Scan on t1
   ->  Hash
         ->  HashAggregate
               Group Key: t2.a
               ->  Seq Scan on t2

四、算子移动

4.1 谓词下推

KingbaseES优化器会尽可能地将WHERE和ON子句中的查询条件以及having子句中的约束条件下推至基表,从而减少扫描返回的记录数量,提升性能

sql 复制代码
EXPLAIN (costs off) SELECT a,SUM(b) FROM t1 GROUP BY a HAVING (SUM(b) < 10 AND a=1);
       QUERY PLAN
-------------------------
 GroupAggregate
   Group Key: a
   Filter: (sum(b) < 10)
   ->  Seq Scan on t1
         Filter: (a = 1)

4.2 UNION外层条件下推

在处理涉及UNION子查询与其他表的连接操作时,该优化规则能够将连接条件深入下推至UNION子查询内部。这一转换使得UNION子查询中的表可以利用索引机制,从而显著提高系统性能。

该转换规则属于基于代价的逻辑优化策略,默认处于关闭状态。在实际应用中需要配置相关参数以启用此功能:

sql 复制代码
SET kdb_rbo.rbo_rule = on; -- 或者 kdb_rbo.rbo_rule = cost;
SET kdb_rbo.enable_push_joininfo_to_union = on;

例如,在连接条件无法下推的情况下,系统需对t2和t3表执行全表扫描。通过应用该规则,我们成功将t3表的扫描方式从全表扫描优化为索引扫描

sql 复制代码
EXPLAIN (costs off) SELECT * FROM t1, (SELECT * FROM t2 union SELECT * FROM t3) v WHERE t1.a=v.a;
                    QUERY PLAN
--------------------------------------------------
 Nested Loop
   ->  Seq Scan on t1
   ->  HashAggregate
         Group Key: t2.a, t2.b
         ->  Append
               ->  Seq Scan on t2
                     Filter: (t1.a = a)
               ->  Index Scan using t3_pkey on t3
                     	Index Cond: (a = t1.a)

五、语义查询优化

5.1 COUNT(DISTINCT)查询优化

KingbaseES优化器当前在处理SUM(DISTINCT)、COUNT(DISTINCT)和MAX(DISTINCT)等去重聚合操作时,无法实施并行运算,仅能采用排序方式实现去重功能。通过开启相应配置参数后,系统将支持并行计算,并提供排序与哈希两种不同的聚合算法供用户选择。

该优化规则属于基于代价的逻辑优化策略,默认处于关闭状态。在实际应用中需要配置相关参数以启用此功能

(1) 参数set kdb_rbo.rbo_rule = on;

(2)参数kdb_rbo.attribute_distinct_value_threshold控制重复度的门限值。该参数范围是[0.0, 1.0]。默认值是0.1表示对于去重列,当100条记录去重后的个数小于10条,则使用该规则。

例如,当a列的重复度很低时,强制使用该规则可能导致性能下降。当a列的重复度很高时,该规则可以提升查询的性能

sql 复制代码
EXPLAIN (costs off) SELECT COUNT(DISTINCT a) FROM t1;
            QUERY PLAN	
----------------------------------
 Aggregate
   ->  Group
         Group Key: t1.a
         ->  Sort
               Sort Key: t1.a
               ->  Seq Scan on t1

5.2 distinct消除

当需要去重的列为非空且满足唯一性约束条件时,系统将能够省略去重操作。

这一优化策略属于启发式规则,需确保参数kdb_rbo.rbo_rule设为ON以启用该特性。 例如,在表t3中,若列a为主键,则对a和b两列进行去重操作时,系统会自动消除冗余的去重步骤

sql 复制代码
EXPLAIN (verbose,costs off) SELECT DISTINCT a,b FROM t3;
      QUERY PLAN
-----------------------
 Seq Scan on public.t3
   Output: a, b

5.3 消除无用的目标列

当子查询的目标列未出现在最终的输出结果中且未被上层查询引用时,KingbaseES优化器会将该目标列设置为空值(NULL)。这一特性特别适用于目标列包含高耗时操作(如函数调用或复杂表达式)的情况,能够有效提高SQL语句的执行效率。

需要注意的是,此功能仅对目标列为immutable或者stable属性的函数有效。例如,在下面的例子中,sum()函数具有不变态属性,因此可以成功转换为NULL;而random()函数由于是volatile属性,则不会进行任何转换操作

sql 复制代码
EXPLAIN (verbose,costs off) SELECT COUNT(1) FROM (SELECT SUM(b),RANDOM() FROM t2);
               QUERY PLAN
----------------------------------------
 Aggregate
   Output: count(1)
   ->  Result
         Output: NULL::bigint, random()

将函数的属性设为易变(volatile)时,会导致无法使用并行处理、不能创建基于该函数的索引,并且无法消除无用的目标列。如果用户确认特定函数对相同的输入值总能返回相同的结果,则可以手动将其属性修改为"稳定(stable、immutable)"以获得更好的优化效果。例如,执行命令"ALTER FUNCTION test1(INT) IMMUTABLE;"即可完成属性修改。

5.4 分组键值消除

对于包含主键的表,若分组字段包含主键,则该表中出现在分组字段中的其他列均可被移除。例如,在下面的例子中,执行计划仅保留主键a作为分组键

sql 复制代码
EXPLAIN (verbose,costs off) SELECT a, b, COUNT(1) FROM t3 GROUP BY a,b;
         QUERY PLAN
-----------------------------
 HashAggregate
   Output: a, b, count(1)
   Group Key: t3.a
   ->  Seq Scan on public.t3
         Output: a, b

结语

KingbaseES的查询优化已超越传统数据库范畴,进入智能优化新时代。在这场没有硝烟的性能革命中,KingbaseES用实践证明:国产数据库不仅能实现技术平替,更能通过深度创新建立性能优势,在稳定性、安全性和智能化之间取得完美平衡

相关推荐
我真的是大笨蛋2 小时前
InnoDB行级锁解析
java·数据库·sql·mysql·性能优化·数据库开发
skywalker_112 小时前
Java中异常
java·开发语言·异常
2501_940315262 小时前
航电oj:首字母变大写
开发语言·c++·算法
没有天赋那就反复2 小时前
JAVA 静态方法
java·开发语言
Thomas_YXQ2 小时前
Unity3D在ios平台下内存的优化详解
开发语言·macos·ios·性能优化·cocoa
山茶花.2 小时前
SQL注入总结
数据库·sql·oracle
咸甜适中3 小时前
rust的docx-rs库,自定义docx模版批量生成docx文档(逐行注释)
开发语言·rust·docx·docx-rs
浒畔居3 小时前
泛型编程与STL设计思想
开发语言·c++·算法
Fcy6483 小时前
C++ 异常详解
开发语言·c++·异常