课程视频

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

在同一次执行中使用新信息

第 18 讲依据指定阅读整理。执行前选计划时,优化器可能不知道真实选择率、远端响应速度或可用内存;执行过程中这些信息逐渐出现,自适应查询处理(Adaptive Query Processing,AQP)尝试用它们调整剩余工作。

Adaptive Query Processing in the Looking Glass实际发表于 CIDR 2005,课程文件名仍带有 2015。本文按 PDF 内的出版信息标注年份。论文比较基于计划、基于路由和连续查询三类方法。

赞助商

什么时候值得重新优化

设教学查询先过滤订单,再与客户、商品连接。优化器预期过滤后有 100 行,某个中间结果物化后发现实际有十万行。继续按小输入设计的索引 Nested Loop 可能很贵,重新规划剩余 Join 就可能有收益。

要比较的是未来还可以节省的工作与修正开销:

1
2
剩余执行成本的预期下降
> 监控 + 重新优化 + 状态转换 + 无法复用的额外工作

已经完成的扫描无法回收,只能决定它的结果是否能够复用。看到估计错误后立刻重启整个查询,可能比坚持原计划更慢。

Plan-Based:保留计划骨架,寻找修正点

基于计划的方法仍使用一棵或一张执行计划。执行器监控重要节点,在安全位置暂停、取得真实统计,然后优化尚未完成的部分。

物化边界是一种自然修正点。中间结果已完整存储,真实行数和部分分布也已知,可以把它作为新的输入关系。但物化本身消耗内存、I/O 和延迟,并会打断流水线。

局部自适应算子则预设几个实现,在输入规模确定后选择。例如在适合小输入与适合大输入的 Join 路径之间选择。它的状态转换范围较小,能改变的计划范围也较局部。

策略调整范围主要成本
局部算法选择一个节点的实现多路径准备、局部状态维护
中途重新优化尚未执行的子计划编译、物化或状态迁移
完整重启大部分查询工作放弃未能复用的已执行工作

Plan Migration 为什么困难

执行状态包含扫描位置、哈希表、聚合状态、已产生的输出与未处理的输入。改变计划必须保持结果完整性,尤其要保证已返回的行不会重复,尚未处理的行不会丢失。

比如从一个 Hash Join 切换到另一个 Join 顺序,旧哈希表保存的是特定输入与键,新的方案可能需要另一种组织。状态可以全部复用、部分转换,也可能需要重建;这些行为决定修正成本。

查询已向客户端输出结果时,状态迁移通常比尚未输出的阻塞算子更复杂。带副作用、不确定性或特殊事务语义的操作,还需要检查改变求值顺序是否合法。

物化边界上的一次重优化决策

假设订单过滤已经完成,物化出十万行中间关系 M。原来的剩余计划使用逐行索引探测,预计还需花费 8 秒;基于 M 的真实规模重新优化后,批量 Hash Join 预计花费 2 秒。监控和统计整理花费 0.1 秒,优化花费 0.3 秒,构造额外状态花费 0.4 秒,则预计未来收益为 8 − 2 − 0.1 − 0.3 − 0.4 = 5.2 秒。

这里比较的是尚未支付的剩余工作,前面扫描产生 M 的时间在两种选择里都已支付。若新方案需要重新读取基本表并额外花费 6 秒,这项成本也要算入,切换就可能失去收益。数字均为教学设定,实际收益预测仍有不确定性。

安全性也需一起验证。如果 M 是本次事务里已经得到的稳定输入,新计划应继续使用相同的查询语义和可见性。若改为重读基本表,要检查并发修改与隔离级别是否会改变结果。已向客户端发送的输出还需要避免重发,因此在尚未产生最终输出的物化边界切换通常更容易管理。

用成本与选择率安排两个过滤

设过滤 P 每行成本为 1,保留率为 0.1;过滤 Q 每行成本为 10,保留率为 0.5。暂时假设二者独立、纯函数且可以合法交换顺序,对一行原始输入:

1
2
P → Q:1 + 0.1 × 10 = 2
Q → P:10 + 0.5 × 1 = 10.5

先执行便宜且筛选强的 P,平均工作明显更少。运行时若观察到 P 的保留率升到 0.99,第一种顺序成本会变为 10.9,第二种为 10.5,原来的排序就需要重新比较。

有相关性时,应观察过滤后剩余数据上的条件选择率;涉及可能抛错、非确定性或有副作用的函数时,还要先证明重排合法。Routing-Based 方法把这种决策下沉到元组或批次,同时用状态记录已经完成的操作。

Routing-Based:逐批决定经过哪些算子

Eddies 等基于路由的方法把元组送入一组可用算子,根据观察到的处理成本与过滤效果调整路线。比如两个独立过滤条件,一个代价低、筛选强,另一个代价高、筛选弱,可以优先尝试前者。

元组需要记录哪些操作已经完成、哪些操作仍需执行。路由器还要保留算子的先后约束:某个 Join 的输入列尚未产生时,不能先进入那个 Join。

这种细粒度适应可以响应局部变化,也增加路由、标记与状态管理开销。固定计划能够提供稳定流水线和缓存局部性;频繁改变路线可能损害这些优势。论文将这些方法放到相同维度比较,而非给出适用于所有场景的一种选择。

Continuous Query:计划会运行很久

连续查询不断处理数据流,输入分布和到达速率可能随时间变化。某个早期观察形成的选择率,无法一直代表后续窗口;系统还要考虑积累的状态和延迟约束。

与短查询相比,长时间运行的计划更有机会摊薄修正成本。但窗口 Join、聚合和状态迁移仍需保证时间语义与结果一致。修改策略时需要说明新的输入从哪里开始采用它,旧状态如何继续处理。

主动为未来修正留下空间

综述提出 Proactive Re-optimization 的方向:初始优化时就考虑将来可能发生重优化,选择更易修正或更稳健的结构。某条当前预测稍便宜的路径如果依赖非常脆弱的假设,未必比有可复用中间结果的方案更合适。

Plan Logging 则记录不同条件下的计划决策,帮助以后复用方案和分析哪些观测值得收集。这使执行前搜索、执行中修正和跨执行反馈形成联系。

Lookahead Information Passing

扩展阅读 Looking Ahead Makes Query Plans Robust介绍 Lookahead Information Passing(前瞻信息传递,LIP)。通过从部分输入构建过滤信息,并提前应用到其他扫描上,减少进入后续 Join 的无效元组。

在星形查询中,维度表经过过滤后只剩少量键,事实表可以先经过这些键摘要的过滤。即使 Join 顺序没有选到最好,提前减少输入也可能缩小损失。过滤摘要的构造、误判和传递成本,以及执行器能否有效应用,仍需要评估。

LIP 说明鲁棒性还可以通过执行机制改善,不一定每次都重建整个 Join 树。它的实验优势有特定数据与查询结构背景,应按原论文的设置阅读。

LIP 在星形查询里的信息传递

假设事实表 sales 有一亿行,维度表分别描述客户地区和商品类别。维度过滤先获得“东部客户编号集合”和“电子商品编号集合”,再为它们构造 Bloom Filter。扫描事实表时先检查两个编号摘要,只有通过的行再进入正式 Join。

假设每个过滤摘要有一定假阳性,通过摘要只代表“可能匹配”,后续 Join 仍需要验证原始键。摘要必须保留真正匹配的行。在可交换的独立过滤场景中,可以根据观测到的过滤能力调整摘要应用顺序,尽早减少数据。

LIP 的收益来自信息提前到达事实表扫描,Join 树本身可以保持不变。若摘要产生得太晚、过滤后的维度几乎包含全部键,或事实表扫描无法高效应用,收益就会减弱。解释鲁棒性实验时,应分别观察过滤前后的事实行数、摘要维护开销与 Join 工作量。

从修正收益回到执行状态

分析一个自适应方案时,先定位新信息在何时出现,再说明它改变哪个决策,最后检查状态怎样复用和结果怎样保持。把监控与切换成本计入端到端运行时间,才能判断它是否降低了估计错误的损失。

参考资料