拉斯维加斯的故事
44.25M · 2026-03-17
在现代企业级应用中,SQL 早已不再是简单的单表查询。为了应对复杂的业务逻辑,开发人员倾向于使用 CTE(公用表表达式)、嵌套子查询、窗口函数和聚集操作来组织数据流程。这种写法虽然提升了代码的可读性和维护性,却往往给数据库优化器带来了“隐形炸弹”——尤其是在 连接条件无法有效下推到子查询内部 的场景下,中间结果集的膨胀会直接拖垮整个查询性能。
本文从一个真实客户案例出发,深入剖析复杂查询中因连接条件下推失败导致的性能瓶颈,并介绍金仓数据库在 V009R002C014 版本中引入的 基于代价模型的连接条件下推(Cost-based Join Predicate Pushdown) 方案。该方案通过“语义等价性保障”与“代价模型决策”的双重约束,在保证结果正确的前提下,实现了数量级的性能提升。
在许多业务系统中,SQL 往往呈现以下模式:
例如:
SELECT *
FROM (
SELECT DISTINCT s1.a, s1.b
FROM s1
) s
JOIN s2 ON s.s1a = s2.s2a
WHERE s2.b = 3;
从业务语义上看,这个查询逻辑清晰:先对 s1 去重,再与 s2 连接并过滤 s2.b = 3 的数据。但从执行层面分析,隐患巨大:
s 必须对 s1 执行全表扫描和去重操作;WHERE s2.b = 3 的高选择性条件无法“穿透”到子查询内部;问题的核心并非连接本身,而是 过滤发生得太晚。
将连接条件下推到子查询内部,直观上能有效解决上述问题。但数据库内核实现这一优化,需要跨越两道关卡:
连接条件下推改变了谓词生效的位置,若处理不当,可能改变 SQL 的最终语义。尤其在以下场景中,下推必须格外谨慎:
GROUP BY)或窗口函数;DISTINCT、UNION 等集合操作;因此,并非所有连接条件都能安全下推,必须建立严格的等价性判定规则。
即便语义上等价,下推也未必总是“划算”:
结论很明确:连接条件下推不仅要 “能推”,更要 “值得推”。
在面对上述 SQL 时,传统优化器通常采用一种保守的执行策略:
这种策略的致命伤在于:外层的高选择性条件无法反作用于子查询的数据扫描阶段。当子查询本身计算复杂且数据量大时,这一路径几乎必然成为性能瓶颈。
金仓数据库在最新的 V009R002C014 版本中,针对上述痛点设计了一套 基于代价的连接条件下推 机制。整个决策过程分为两个阶段,确保优化既安全又高效。
本阶段的目标是识别 绝对安全 的下推机会,而非盲目下推。优化器会:
只有通过等价性校验的谓词,才会被改写为参数化过滤条件,注入到子查询的扫描或过滤阶段。这一步的核心是确保:下推后的结果与原 SQL 完全一致。
通过等价性校验后,优化器不会立即选择下推,而是进入代价评估环节:
若代价模型判定下推收益不足甚至可能引发性能回退,优化器会自动放弃下推,转择其他执行路径。这一步保证了:下推后的计划真正更快。
下图概括了整个决策流程:
连接谓词
│
▼
┌───────────────┐
│ 等价性判定 │
│ • 子查询结构 │
│ • 语义安全 │
└───────────────┘
│ 通过?
▼
┌───────────────┐
│ 代价模型评估 │
│ • 下推前后代价│
│ • 参数化开销 │
└───────────────┘
│ 值得?
▼
执行下推或保留原计划
测试 SQL:
SELECT *
FROM (SELECT DISTINCT * FROM s3) s3
JOIN s1 ON s1.s1a = s3.s3a;
作为对比,某主流商业数据库(D厂商)在相同 SQL 下的执行时间为 1.62 ms(采用 Hint 强制 Nested Loop),远高于金仓下推后的耗时。
测试 SQL(包含 UNION、DISTINCT、窗口函数、多层子查询):
EXPLAIN ANALYZE
SELECT *
FROM (
SELECT *
FROM (
SELECT DISTINCT * FROM s3
UNION
SELECT DISTINCT * FROM s3 a
) s3
JOIN s1 ON s1.s1d = s3.s3a
) s
JOIN (
SELECT *
FROM (
SELECT s3a, SUM(s3b) OVER (PARTITION BY s3a) s3d
FROM s3
) s3
JOIN s1 ON s1.s1a = s3.s3a
) j ON s.s3d = j.s3a;
通过对比可见,下推后的执行计划有效避免了中间结果的爆炸性增长,将“先计算后过滤”转变为“边过滤边计算”,极大释放了系统性能。
复杂查询中的连接条件下推,远非简单的规则改写,而是一项典型的 成本驱动型优化:
金仓数据库通过 等价性保障 + 基于代价的决策 的组合设计,在安全的前提下最大化连接条件的过滤能力,显著减少子查询阶段的数据扫描与中间结果规模,在复杂 SQL 场景中实现了数量级的性能提升。
这类优化对于 OLAP、混合负载以及复杂报表型查询尤为关键。未来,随着数据规模和查询复杂度的持续增长,代价驱动的智能下推技术将成为查询优化器演进的核心方向之一。