课程视频

截至 2026 年 10 月 2 日,所给 B 站收藏夹收录到第 14 讲,官网 Schedule 未提供本讲视频链接。本页依据指定论文整理。

用已经执行过的查询修正后续选择

第 17 讲依据官网指定论文整理。前面的统计与估计用于执行前预测,反馈优化则把执行时观察到的行数和成本带回后续优化。它主要改善再次出现的查询或相似子表达式。

本讲把两条思路放在一起学习:LEO 修正优化器对规模的认识,Plan Stitch 复用多个历史计划中的有效片段。一个改变估计输入,一个改变可利用的历史计划候选。

赞助商

LEO 怎样把执行行数映射回逻辑表达式

LEO — DB2’s LEarning Optimizer收集执行反馈,比较估计基数与实际基数,再把信息关联到后续优化可识别的逻辑对象。

例如一个教学查询在 Join 处估计 100 行,执行得到十万行。记录“某个物理节点输出十万行”还不够,因为下一次优化可能采用不同物理算子或 Join 树。需要保存对应的表、谓词与逻辑子表达式,使优化器能够识别同一估计问题。

1
2
3
4
编译:逻辑表达式 → 估计 → 物理计划
执行:物理节点 → 真实行数
反馈:物理节点映射回逻辑表达式
下次优化:读取并使用相关反馈

相关性或 Join 倾斜导致的错误,可能无法仅靠更新单列直方图修复。表达式级反馈提供了另一种输入,但它也有覆盖范围:未执行过的候选和新参数区域通常缺少观测。

实际行数要区分完整输出与部分观察

LIMIT 提前停止、查询取消或短路执行时,看到的行数未必是完整结果规模。Nested Loop 内侧节点的每次输出行数与所有循环累计输出也不同。

采集系统需要说明观测是否完整、节点调用次数、参数上下文和数据版本。否则一次“只取前十行”的执行,可能被错误地当成查询总共只有十行。

Nested Loop 的每次输出与累计输出

设 Nested Loop 外侧实际有 1000 行,内侧索引节点被调用 1000 次,每次平均返回 5 行。内侧总计输出约 5000 行。如果计划显示工具给出的是“每次调用平均 5 行”,而反馈系统把 5 当成全部 Join 的基数,就会把结果低估三个数量级。

反过来,把累计 5000 行写入“单次参数化探测”的估计位置,也会大幅高估一次访问成本。反馈记录要带调用次数,并说明观测单位:每个绑定的输出、全部绑定的累计输出,以及逻辑 Join 的最终输出。

若外侧被 LIMIT 截断,只执行了 20 次探测,那么累计观察到的 100 行只描述已经执行的这部分工作。系统可以把它作为部分信号,却不能直接断言完整查询只返回 100 行。提前终止、过滤短路与取消都需要观测完整性标记。

整条计划回退的局限

假设新建索引后,查询选择了计划 P2,但它在某个 Join 上严重低估,运行变慢。系统可以回退到历史计划 P1,前提是 P1 仍然合法。

然而 P2 的某个索引访问片段可能确实很好,P1 的大部分 Join 结构也可能更好。整条计划回退无法同时利用这两部分收益。Plan Stitch研究如何从同一查询的历史执行计划中组合一个新的完整方案。

Plan Stitch 怎样限定组合空间

Plan Stitch 先识别历史计划中对应相同逻辑表达式的物理候选,然后在受约束的空间里重新搜索。候选物理算子必须来自已有执行记录,并在当前配置下仍然合法。

下面的成本为教学设定,假设片段语义、属性和输入上下文兼容:

历史计划计算 ABC 的片段与 D 连接的片段总成本
P150100150
P212020140
可兼容拼接502070

拼接能够得到没有整体执行过的新计划,但组成片段已有观测。实际算法需要按逻辑表达式识别片段,满足排序、参数依赖等物理条件,再通过动态规划比较组合。

这里的最优性限定在论文定义的历史候选空间与成本组合模型中。它无法自动发现一条历史上从未出现过的访问路径,也不能仅靠“来自历史计划”保证所有片段可以接起来。

观测成本为什么不能随意相加

一个节点记录的 elapsed time 可能包含子节点工作,也可能与其他节点并行重叠。把这类数字直接加起来,会重复计费或忽略并行关系。输入行数变化后,片段的成本也可能变化。

Plan Stitch 论文讨论执行成本的记录、可组合性和计划验证。学习时要明确它使用哪种成本指标、如何避免重复计算,并理解其平均执行成本与参数实例的关系。

上面的示例刻意固定兼容条件,作用是解释“局部最佳可能来自不同计划”的动机。用于真实系统时,还应确认历史统计能代表当前数据与工作负载。

拼接片段时,先检查它的边界

假设历史片段 P1 计算 ABC,输出按 join_key 排序;片段 P2 的父节点使用 Merge Join,要求这项顺序。两者在逻辑结果和属性上可能兼容。若更便宜的另一 ABC 片段输出无序,就需要加 Sort 后重新比较,总成本不能仍写成无序片段的原始成本。

还有更隐蔽的参数边界。历史 Index Seek 可能依赖外侧 D 的某个值,而新组合希望先独立计算 ABC。这个片段在缺少参数时无法运行,不能当成普通独立访问路径接入。数据位置、输出列映射和唯一性声明也可能成为组合约束。

可以将片段接口写成教学形式:

1
2
3
4
语义:计算哪个逻辑子结果
输入:子结果、外部参数及所需属性
输出:列、顺序与分布
观测:成本单位、输入规模、执行次数与环境

这些字段说明了为什么“从每条历史计划各拿最快的节点”无法直接组成正确计划。动态规划需要比较一组边界兼容的实现,再把局部观测放到论文定义的可组合成本中。

反馈修正与计划拼接还可以互补。先用 LEO 一类反馈改善某个逻辑表达式的基数,再让优化器发现新的物理结构;这些新结构经过执行积累观测以后,又扩充 Plan Stitch 的候选来源。不同阶段应分别记录收益,避免把重新编译与片段复用的效果混在一起。

反馈也需要有效范围

参数化查询同一模板的执行规模可以差很多,一次地区热点查询的反馈不应无条件覆盖全部地区。数据更新、索引变化和资源配置变化也会改变历史观测的参考价值。

可以把有效范围理解为表达式、参数特征、数据版本和运行环境的组合。收集更多反馈能增加覆盖,也增加存储、匹配和筛选成本。对噪声很大的观测,还需要重复样本或其他判断依据。

如何判断反馈带来了收益

先说明反馈改动的是基数输入、整体计划选择,还是历史片段组合。然后比较计划结构和端到端运行时间,并观察未受益或退化的参数区域。

反馈只对已观察部分提供信息。它与准确统计、合法规则和运行时机制可以互补。下一讲 运行时处理把修正时机推进到同一次查询执行中,重点将变为剩余工作与状态迁移。

参考资料