数据库-GaussDB-基础篇之子查询调优
GaussDB 子查询调优详解
一、子查询背景介绍
业务应用在操作数据库时,会大量使用子查询。相比直接多表 JOIN 写法,子查询在语义上更加独立完整,逻辑思路清晰,在复杂 SQL 场景下可读性更强,因此被广泛使用。
在 GaussDB 内核的 SQL 解析阶段,会根据子查询所在位置,将其分为 SubQuery(子查询) 和 SubLink(子链接) 两大类:
- SubQuery(子查询)
对应查询解析树中的RangeTblEntry。
通俗理解:写在FROM后面独立的SELECT语句,也就是派生表。
本质:范围表,当作一张临时表参与 JOIN 运算。 - SubLink(子链接)
对应查询解析树中的表达式节点。
通俗理解:写在WHERE/ON条件、targetlist(SELECT返回字段)里面的子查询。
本质:表达式,不是表,作用是返回一个值。
SubLink 的 7 种细分类型
表格
| SubLink 类型 | 对应 SQL 语法 | 说明 |
|---|---|---|
exist_sublink |
EXISTS / NOT EXISTS |
存在性判断,优化器重点优化,支持子链接提升 |
any_sublink |
ANY(子查询)、IN(子查询) |
支持< > =运算符;IN / NOT IN (select ...)归为此类 |
all_sublink |
ALL(子查询) |
支持< > =运算符 |
rowcompare_sublink |
record op (select ...) |
行比较子链接 |
expr_sublink |
(SELECT single target item ...) |
标量子查询 SSQ,返回单行单列 |
array_sublink |
ARRAY(select ...) |
数组子链接 |
cte_sublink |
WITH query(...) |
CTE 公共表表达式 |
优化说明:
exist_sublink、any_sublink:GaussDB 优化器针对这两类做了专项优化,支持子链接提升(Sublink Release);expr_sublink:部分场景支持提升,但使用灵活,极易产生性能问题;可通过 GUC 参数rewrite_rule关闭该类子链接的提升优化。
子查询两大类别
- 非相关子查询(None-Correlated SubQuery)
子查询执行不依赖外层查询任何字段,具备独立性,可以提前一次性求解,子查询计划优先于外层查询执行。
select t1.c1,t1.c2
from t1
where t1.c1 in (
select c2
from t2
where t2.c2 IN (2,3,4)
);
执行计划示例:Hash Right Semi Join,一次性算出子查询全部结果集合。
- 相关子查询(Correlated SubQuery)
子查询引用外层表字段,无法独立执行;外层表每扫描一行,就需要带入外层字段值执行一次子查询。
select t1.c1,t1.c2
from t1
where t1.c1 in (
select c2
from t2
where t2.c1=t1.c1 AND t2.c2 IN (2,3,4)
);
性能风险:无法提升时,执行计划生成
SubPlan + Streaming(BROADCAST),内层表数据量大时性能急剧恶化。
专有名词
- Sublink 提升(Release / 子链接提升):内核将表达式形式的 SubLink,重写转换为 Join(半连接 / 外连接),消除 SubPlan,不再循环执行子查询,规避 DN 之间广播数据,是子查询调优的核心目标。
- SSQ:ScalarSubQuery,标量子查询,属于
expr_sublink,返回单行单列标量值。 - CSSQ:Correlated-ScalarSubQuery,包含相关条件的标量子查询。
二、支持 Sublink 提升的场景
满足以下条件,优化器可自动把子链接改写为 JOIN,消除 SubPlan:
- IN any_sublink(非相关子查询)
子查询不引用外层表列,无易变函数,可提升为Nested Loop Semi Join。 - EXISTS exist_sublink 等值相关子查询
WHERE 条件存在外层表等值关联;子查询其余部分不能再引用外层表。
限制条件:必须包含 from 子句;不能带 with、聚集函数、group by、limit、窗口函数、having、易变函数;提升为Hash Semi Join。 - 带聚集函数的等值相关子查询
- where 条件:外层表等值关联,多个条件仅允许
AND连接; - select 输出:仅允许 1 列,聚合函数只能使用 max/min 等,不能使用 count;聚合参数不能来自外层表;
- 禁止 group by、having、集合操作;仅支持 Inner Join。
- WHERE OR 子句中的 EXISTS 相关 SubLink
WHERE 条件通过 OR 连接多个 EXISTS 相关子查询,满足条件可逐步改写为左连接,实现提升。 - TargetList 返回列中的 SSQ 标量子查询(不带 count)
SELECT 返回字段内的相关标量子查询,可提升为Right Join;
原理:不匹配场景需要补 NULL,使用右外连接,消除SubPlan+Broadcast。
三、不支持 Sublink 提升的场景(高风险)
不属于上面场景,均无法自动提升,执行计划生成SubPlan + Broadcast,属于性能隐患:
- TargetList 中带 count ()的相关子查询,内核无法自动提升;
- 非等值相关条件(>、<)SubLink:内核原生不支持自动提升
- 人工改写思路:两次 JOIN(CorrelationKey + rownum 自关联)
- 局限:GaussDB 无全局唯一 rowid;可尝试
xc_nodeid + ctid模拟 rowid,但xc_nodeid重复率高,join 效率差;xc_node_id+ctid不能作为 hash join 关联键;业务层优先规避这类 SQL。 - 改写须知:count 场景用
CASE WHEN补 0;非 count 场景补 NULL;CTE 改写方案,支持 sharescan 时性能更优。
- 子查询内部包含两层表 JOIN;
- 子查询包含 with 子句、group by、having、集合操作、窗口函数、limit;
- 子查询包含易变函数;
- 多层嵌套复杂相关子查询。
四、优化实操示例
示例 1:表属性 + 索引优化
内层子查询表改为replication复制表,在关联过滤字段建立索引,减少 DN 间广播开销。
create table master_table (a int);
create table sub_table(a int, b int);
select a from master_table group by a having a in (select a from sub_table);
示例 2:SQL 改写消除 SubPlan
原始 SQL 存在SubPlan + Broadcast,性能差;改写为 exists,计划变为Hash Semi Join,消除 SubPlan。
-- 改写前
select * from master_table as t1 where t1.a in (select t2.a from sub_table as t2 where t1.a = t2.b);
-- 改写后
select * from master_table as t1 where exists (select t2.a from sub_table as t2 where t1.a = t2.b and t1.a = t2.a);
五、执行计划识别(排查手段)
表格
| 计划特征 | 含义 | 结论 |
|---|---|---|
| SubPlan + Streaming(BROADCAST) | 子查询未提升,外层每行循环执行子查询,内表广播到所有 DN | ⚠️ 性能风险,需要优化 |
| Hash Semi Join / Nested Loop Semi Join / Hash Right Join | 子查询成功提升为 Join,无 SubPlan | ✅ 优化完成 |
六、核心调优结论
- 子查询调优核心目标:尽可能触发 Sublink 提升,消灭 SubPlan 和 Broadcast;分布式 GaussDB 中 Broadcast 是主要性能杀手。
- IN、EXISTS 等值相关子查询更容易被内核提升,优先推荐
EXISTS写法。 - 带 count、非等值、多表嵌套 join 的相关子查询,内核提升能力弱,必须人工改写或者业务规避。
- TargetList 标量子查询:不带 count 可提升;带 count 无法自动提升,需要 case when 改写处理空值。

浙公网安备 33010602011771号