什么是基于成本的优化器?
基于成本的优化器(CBO)是数据库中的决策引擎:它探索合法执行计划,估算每个候选方案的工作量,并在自身模型下选择所找到的最低成本计划。常见输入包括已绑定查询、Schema与索引、表与列统计信息、约束、分区、可用算子、成本常量和配置;输出则是可执行的物理计划。
“最低成本”并不承诺实际耗时一定最短。优化器只会搜索巨大计划空间中的有限子集,用估算而不是未来事实做判断,并以引擎特定单位表示资源权衡。当统计信息、参数、缓存状态、并发、内存或网络条件偏离假设时,所选计划即使在模型中合理,也可能表现很差。
基于成本与基于规则的优化有什么区别?
| 方法 | 主要依据 | 典型作用 | 主要限制 |
|---|---|---|---|
| 正确性规则 | 关系等价与合法性 | 安全重写与验证 | 不负责对运行方案排序 |
| 启发式或规则型 | 固定优先级与搜索捷径 | 规范化、简化与剪枝 | 可能忽略数据分布 |
| 基于成本 | 统计信息、估算与资源模型 | 比较物理候选方案 | 模型与估算可能错误 |
| 运行时自适应 | 实测规模或执行反馈 | 执行开始后调整 | 产品与算子支持不一致 |
现代引擎通常组合这些方法。规则可以下推谓词或简化表达式,成本模型再比较扫描、连接与顺序,运行时逻辑还可能改变数据分布或连接策略。查询调优则是另一层:它是由人改进证据、修改受支持约束并验证结果的流程。把每个计划选择都归因于“只有CBO”,会掩盖重要边界。
基于成本的优化器如何工作
- 解析、绑定并确认合法性。
引擎解析名称与类型、检查权限并生成内部表达式。SQL语法正确并不能证明业务语义正确或性能良好。
- 应用保持语义的转换。
可能的重写包括常量折叠、子查询展开、视图合并、谓词移动与冗余操作消除;是否可用取决于引擎和查询形态。
- 估算选择率与基数。
统计信息预测每个条件通过的行比例,以及算子之间流动的行数;这些估算向上传播并影响后续选择。
- 枚举物理候选方案。
优化器考虑可用访问路径、连接顺序、连接算法、聚合、排序、物化、并行与分布方案,同时剪除被视为劣势或探索代价过高的候选方案。
- 估算成本、比较并生成计划。
成本模型组合算子估算工作量与子计划成本,搜索过程保留更有希望的子计划;最终由停止条件或时间预算产出一个可执行计划。
统计信息是证据,不是数据副本
基于成本的优化器不能在规划时扫描每张表,因此依赖紧凑摘要:近似的表与索引大小、行数与页数、不同值数量、NULL比例、高频值、范围、直方图,有时还包括跨列相关性。约束与分区可以提供精确边界。许多系统通过抽样或定期任务刷新统计信息,所以即使刚更新的数据仍然是模型。
选择率回答“有多大比例通过”,基数回答“流出多少行”。在估算为一百万行的输入上,1%的谓词对应约一万行;同样的选择率用于一百行的小表,可能产生完全不同的访问路径选择。
单列直方图无法描述所有关系。城市与邮编、产品与类别、租户与状态都可能相关。如果模型把相关条件当作独立选择率相乘,行数估算可能过低或过高。在引擎支持时,可针对已证实的偏差创建多列统计信息;为所有列组合采集统计既不现实,也会增加维护成本。
查询计划成本不等于耗时
成本模型把估算工作量映射为可比较数值。根据引擎与算子不同,它可能表示顺序与随机页访问、行和表达式处理、索引遍历、排序、哈希、内存压力、临时I/O、并行启动、数据传输或远程往返。有些成本可配置,有些是内部实现;上层计划节点通常包含其所有子节点成本。
- 启动成本估算首行输出前的工作,例如构建或排序输入。
- 总成本估算节点被完整消费时的工作;LIMIT或提前停止会改变重要目标。
- 成本单位主要只在同一优化上下文中有意义,不应直接换算成毫秒,也不应跨引擎比较。
修改全局成本常量是高影响操作。修复一个查询的值可能扭曲成千上万个其他查询。应先证明实际硬件、缓存或网络行为确实与模型严重不符,再测试代表性工作负载、记录作用范围并准备回滚路径。
计划枚举必须权衡搜索质量与规划时间
一个连接多张表的查询可能拥有大量合法连接树,而每棵树又可搭配多个访问路径、连接算法、排序策略、物化方案,以及并行或分布式变体。穷举成本可能超过执行查询本身,所以实际优化器会复用已知最佳子计划、限制转换、剪除劣势方案、应用启发式方法、在改进希望较小时停止,或对超大连接切换搜索策略。
因此,“优化器选择了最低成本”实际表示“在保留的候选方案中选择最低估算成本”,而不是在所有可想象计划中求得数学最小值。一个有希望的计划可能从未生成,也可能因早期基数错误让某个子计划显得昂贵而被剪枝。增加规划工作量可能改善选择,却也会增加编译延迟与计划缓存压力。
CBO如何选择访问路径、连接顺序与算法
| 决策 | 可能适用的情况 | 可能失败的情况 |
|---|---|---|
| 索引查找或扫描 | 符合条件行少、顺序有用或可覆盖数据 | 大量随机查找、聚簇性差或选择率陈旧 |
| 全表或顺序扫描 | 需要较大比例、表很小或顺序读取高效 | 遗漏了高选择性的受支持路径 |
| 嵌套循环连接 | 外部输入小且内部探测便宜 | 外部基数被严重低估 |
| 哈希连接 | 较大等值连接且构建端规模可控 | 构建端溢写、倾斜或网络移动占主导 |
| 合并连接 | 输入已排序或排序可服务后续工作 | 所需排序成本高于其他方案 |
任何算子名称都不是天然好或坏。连接顺序控制中间结果规模,连接算法决定如何组合这些行,访问路径决定基础行如何到达。应结合实际行数、循环次数、读取、溢写、内存、网络、等待与并发评价完整计划。
DBMS中的基于成本优化:假设示例
以下数字仅为说明性示例,不是基准声明。假设一个查询连接`orders`、`customers`和`regions`。统计信息显示`orders`有两千万行,日期谓词保留5%,状态谓词保留10%。如果模型假设两个条件独立,就会预测十万条符合条件的订单,并可能选择以过滤后的订单为构建端进行哈希连接,再连接客户与区域。
但如果近期订单中“open”状态占比异常高,两个谓词组合后实际返回180万行,就出现十八倍低估。这可能导致哈希表溢写、改变适合的构建端、放大下一次连接并延迟首行。SQL没有变化,所选计划在原估算下也自洽;真正断裂的是描述相关性的证据链。
严谨处理方式是:在最早偏离节点比较估算行与实际行,检查统计信息年龄和抽样,调查倾斜与相关性,再测试受支持的目标统计或查询、设计替代方案。在理解估算前强制另一种连接,可能只会掩盖某个参数的症状,却在其他参数上失败。
为什么基于成本的优化器会选择错误计划
- 统计信息陈旧或缺失:表增长、批量加载或分布变化没有进入模型。
- 倾斜与相关性:平均值或列独立假设遗漏热点值与耦合谓词。
- 未知表达式:函数、类型转换、变量或跨列表达式可能迫使引擎采用默认估算。
- 参数敏感性:一个缓存计划被复用于需要显著不同访问路径的参数值。
- 搜索剪枝:有用计划从未生成,或因早期误估而被删除。
- 模型不匹配:配置的I/O、CPU、内存或网络假设不能代表实际负载。
- 运行条件变化:并发、缓存、溢写、锁、远端可用性或自适应行为与编译时不同。
诊断CBO决策的可重复工作流
- 证明结果契约。
比较速度前先固定预期行粒度、重复、NULL行为、排序、精度、时间边界与隔离要求。
- 定位最早的实质估算偏差。
从基础访问向上阅读计划,结合循环次数比较估算行与实际行;后续错误可能只是结果。
- 解释估算来源。
检查统计信息新鲜度、高频值、直方图边界、NULL、表达式、约束、相关性与参数可见性。
- 把偏差连接到物理选择。
说明该估算如何影响扫描、连接顺序、连接算法、内存授予、并行或数据移动,避免调优无关节点。
- 一次测试一个受支持的处理方法。
候选方法包括刷新或扩展统计信息、暴露可搜索谓词、修改有依据的索引或分区设计、处理参数类别,或应用范围受控的计划约束。
- 验证并监控。
跨代表性参数类别比较结果、计划形态、实际工作量、重复延迟、资源、并发与波动,并定义回滚阈值。
参数敏感性会让一个好计划看起来很差
一个返回一行的谓词值与一个返回半张表的值,可能无法共享高效计划。编译时可能使用已知字面值、参数估算、平均分布或未知值默认值,随后缓存计划又服务后续执行。根据引擎不同,处理手段可能包括重编译行为、自定义与通用计划、参数敏感计划功能、查询变体、过滤结构或计划治理。
不要只测试慢字面值,也不要只测试平均值。应定义高频、罕见、空结果、近期范围、历史范围与租户极端等参数类别,记录每类参数获得的计划,并判断编译开销、缓存抖动或吞吐变化是否抵消延迟收益。
把提示与计划基线作为受治理约束
提示、计划指南、基线或强制计划,在事故、已知回归或严格控制的负载中可能有价值,但它们不是对成本模型的免费修复。固定访问路径可能在数据增长后失效,强制连接可能阻止新的优化器改进,计划基线也可能保留已经不再满足原假设的方案。
约束计划前,应记录观察到的失败、测试过的替代方案、受支持语法、准确版本范围、参数覆盖、负责人、监控信号、到期或复查日期与回滚方法。当修正已证实的证据或物理设计更安全且可维护时,应优先采用这些方式。
验证内容不能只有估算成本
| 维度 | 测量内容 | 失败信号 |
|---|---|---|
| 正确性 | 行、重复、NULL、排序与汇总 | 任何未经批准的语义差异 |
| 估算 | 关键节点估算行与实际行 | 实质偏差仍无法解释 |
| 执行 | 读取、CPU、内存、溢写、网络与等待 | 工作转移到另一个有害瓶颈 |
| 延迟与吞吐 | 重复测试的中位数、尾延迟、吞吐与波动 | 代表性参数类别出现回归 |
| 运维 | 编译负载、缓存、写入、锁与发布 | 变更不可观测或不可回滚 |
成本有助于解释优化器为什么偏好某个保留候选方案;实际证据才决定计划是否满足工作负载目标。应明确区分冷热缓存、客户端传输时间、并发和观察窗口,而不是把它们平均成一个看似安心的数字。
在引擎原生CBO测试前审查SQL结构
请准备经过脱敏的完整SQL,并确认预期方言。InfiniSynapse SQL Complexity Checker的可见功能会在浏览器中分析嵌套查询、CTE、连接、窗口函数、聚合与方言特定结构等模式,帮助安排哪些片段应优先由人工审查或检查计划。
该检查器不会执行SQL,不会检查表、索引或统计信息,不会枚举物理计划,也不会计算引擎原生成本或证明性能。复杂度评分是静态启发式结果,不是CBO输出。完成结构审查后,应回到目标数据库获取统计信息、估算与实际计划、代表性参数和受控基准。现有InfiniSynapse SQL查询优化指南提供更广泛的实用调优流程,本地查询优化指南则解释从优化器到验证的完整生命周期。
基于成本的优化器常见问题
什么是基于成本的优化器?
基于成本的优化器是数据库中生成或探索合法执行计划、利用统计信息与成本模型估算每个候选方案工作量,并选择其找到的最低成本计划的组件。这个结果是由估算驱动的决策,不是对“实际运行必然最快”的证明。
基于成本的优化器如何工作?
它会转换已绑定的查询,估算谓词选择率与中间结果基数,枚举访问路径、连接顺序和物理算子,分配模型成本,剪枝搜索空间,并返回一个可执行计划。具体阶段与算法因数据库及版本而异。
基于成本的优化器会使用哪些统计信息?
常见输入包括表与索引大小、行数、不同值数量、NULL比例、高频值、直方图,有时还包括多列统计信息。引擎也可能使用约束、分区、物理排序、存储元数据、运行反馈与系统假设。
规则优化器与基于成本的优化器有什么区别?
规则优化器使用优先级规则或启发式方法,而不会通过数据敏感的成本模型比较所有候选方案;基于成本的优化器使用统计信息与建模后的资源工作量比较方案。现代系统通常组合正确性规则、启发式重写与基于成本的物理选择,而不是只采用一种方式。
为什么基于成本的优化器会选择慢计划?
陈旧或缺失的统计信息、数据倾斜、相关谓词、参数敏感性、难以估算的表达式、不完整搜索、与环境不匹配的成本常量、计划缓存复用,或运行条件偏离编译假设,都可能误导优化器。
更低的估算成本是否一定表示查询更快?
不一定。估算成本是引擎特定模型的输出,常使用抽象单位;它只在一组假设下比较候选方案,不是耗时,也通常不能跨引擎、版本或无关优化上下文比较。应使用安全的实际计划证据和代表性基准验证。
何时应使用优化器提示或计划基线?
只有在确认结果正确、复现错误选择、检查统计信息与受支持的设计修复,并测试代表性参数类别之后才考虑。提示或基线是受治理的约束,应有负责人、版本范围、监控和回滚,因为数据与优化器行为会变化。
InfiniSynapse SQL Complexity Checker能计算优化器成本吗?
不能。其可见功能只在浏览器中静态分析SQL结构和启发式复杂度;它不执行SQL,不检查Schema、索引或统计信息,不枚举引擎计划,也不计算引擎原生成本。可用它在数据库测试前安排结构审查优先级。
