Vik Fearing:依赖的暗示

Vik Fearing:依赖的暗示

💡 原文英文,约5000词,阅读约需19分钟。
📝

内容提要

SQL标准不应加入执行提示,因为关系模型要求数据独立性,查询不应暴露索引等存储细节。提示会将存储假设固化进应用,数据变化后反而有害。PostgreSQL长期拒绝提示,最终在查询之外实现计划控制,如pg_plan_advice;更好的做法是用CREATE STATISTICS修正优化器的数据认知,而非指令。

🔎

延伸解读

数据独立性与索引依赖

文章引用Codd 1970年的论文,指出关系模型的核心目标之一是消除索引依赖。索引本质上是性能导向的冗余结构,会随数据活动变化而增删。若在SQL中嵌入索引提示,应用程序便与特定索引绑定,索引一旦移除或变更,程序可能无法正确运行。这正是Codd当年批评IDS系统的缺陷,也是现代SQL标准拒绝提示的根本原因。

提示的代价:升级与扩展风险

文章强调,查询提示会将存储假设固化到应用代码中,随版本部署而难以更改。当数据规模从万行增长到千万行,或数据库升级后优化器改进,原本有效的提示可能变成性能负担。Oracle和SQL Server的文档均警告提示应作为最后手段,PostgreSQL社区也指出提示会干扰升级并掩盖优化器缺陷。

PostgreSQL的替代路径:计划建议与扩展统计

PostgreSQL长期拒绝在SQL中实现提示,但通过pg_plan_advice等工具在查询之外提供计划控制,并允许通过pg_stash_advice存储建议。更根本的解决方案是CREATE STATISTICS,它修正优化器对数据依赖关系的认知,而非直接指令计划。例如,为相关列创建依赖统计后,优化器能更准确地估算选择率,从而自主选择更优计划,且随数据变化自动更新。

MATERIALIZED:一个妥协的提示

文章指出,PostgreSQL 12引入的MATERIALIZED关键字实际上是一个显式提示,用于强制CTE不被内联,从而保留优化屏障。这违背了Date和Darwen的“无内部级构造”原则,因为它在查询文本中嵌入了计划决策。尽管它解决了用户对优化屏障的依赖,但也表明完全避免提示在工程实践中十分困难。

❓

Q&A

为什么SQL标准不应该加入执行提示(hints)?

因为关系模型要求数据独立性,查询不应暴露索引等存储细节。提示会将存储假设固化进应用,数据变化后反而有害。Codd在1970年就指出索引依赖是数据语言需要消除的缺陷之一。

PostgreSQL为什么长期拒绝在SQL中加入提示?

PostgreSQL社区认为提示会干扰升级(今天的提示在升级后可能变成反性能),且会掩盖优化器问题,使用户不报告bug。官方立场是:不欢迎基于“别人有所以我也要有”的提示方案,但欢迎能避免已知问题的创新设计。

PostgreSQL最终如何实现查询计划控制而不把提示写进SQL?

PostgreSQL 19将引入pg_plan_advice和pg_stash_advice两个contrib模块。pg_plan_advice把已完成的计划转成可反馈给优化器的指令(如JOIN_ORDER、SEQ_SCAN),可存储在查询之外,通过pg_stash_advice按查询标识符自动应用,查询文本本身不被修改。

当优化器因数据相关性估计错误而选错计划时,更好的解决办法是什么?

使用CREATE STATISTICS修正优化器对数据的认知,而不是写提示。例如对相关列创建依赖统计(DEPENDENCIES),让优化器知道列不独立,从而修正选择率估计。统计对象是DDL,存在于目录中,能修正所有涉及这些列的查询,且随ANALYZE更新而适应数据变化。

在查询中写提示会带来哪些具体风险?

提示会把存储假设(如使用哪个索引、如何扫描)固化到应用代码中,随应用版本部署,不随数据变化而调整。当数据规模或分布改变时,原本正确的提示可能变成错误的,导致性能下降。此外,提示混淆了查询作者与DBA的角色分工。

其他数据库(如Oracle、SQL Server、MySQL)的提示系统有什么共同警告?

Oracle、SQL Server和MySQL的官方文档都建议谨慎使用提示,并推荐使用SQL调优顾问、SQL计划管理、查询存储等工具来替代。Oracle甚至说“强烈建议使用那些工具而不是提示”,SQL Server称提示应“仅作为最后手段”。

🏷️

标签

➡️

继续阅读