DeepSeek总结的PostgreSQL 19查询提示功能
来源:https://thebuild.com/blog/2026/04/21/hints-part-3-advice-not-orders/
提示,第三部分:建议,而非命令
我对pg_plan_advice早期预览版的反应。
Robert Haas 的pg_plan_advice补丁集(提议用于 PostgreSQL 19)是第二部分中那场持续了二十年的争论所达成的(或者说试图达成的)结果。它不是将pg_hint_plan引入核心。它是一个不同的东西,具有不同的机制、不同的范围,并且对“这与 Oracle 风格的提示有何不同”这个问题给出了不同的答案。
该补丁引入了三个contrib模块:
pg_plan_advice—— 核心机制。通过EXPLAIN生成计划建议字符串,并通过一个 GUC 来应用它们。pg_collect_advice—— 一个示例扩展,展示了如何扩展建议收集功能。pg_stash_advice—— 一个示例扩展,展示了如何扩展建议应用功能,通过查询标识符将存储的建议字符串与查询匹配。
这种三部分拆分很重要。pg_plan_advice被刻意设计得极简——Haas 将这种设计描述为“机制,而非策略”——而另外两个模块则演示了你可以在此基础上构建什么。该架构假设其他扩展将完成有趣的工作。
生成建议
加载扩展并使用新的PLAN_ADVICE选项运行EXPLAIN:
LOAD'pg_plan_advice';EXPLAIN(COSTSOFF,PLAN_ADVICE)SELECT*FROMjoin_fact fJOINjoin_dim dONf.dim_id=d.id;QUERY PLAN ------------------------------------ Hash Join Hash Cond: (f.dim_id = d.id) -> Seq Scan on join_fact f -> Hash -> Seq Scan on join_dim d Generated Plan Advice: JOIN_ORDER(f d) HASH_JOIN(d) SEQ_SCAN(f d) NO_GATHER(f d)“Generated Plan Advice” 部分就是新的输出。它以一种紧凑、结构化的形式描述了计划形态:连接顺序、连接方法、每个表的扫描方法、并行决策。这就是建议字符串。
应用建议
通过一个 GUC 将建议交还给规划器:
BEGIN;SETLOCALpg_plan_advice.advice='HASH_JOIN(d)';EXPLAIN(COSTSOFF,PLAN_ADVICE)SELECT*FROMjoin_fact fJOINjoin_dim dONf.dim_id=d.id;规划器产生相同的哈希连接计划,并且EXPLAIN输出现在在生成的建议旁边包含一个“Supplied Plan Advice”部分,用/* matched */或未匹配注释每个提供的指令。你可以准确地看到规划器采纳了你的哪些建议。
你不必传回完整的建议字符串。只关心你关心的部分也可以。如果你只想固定连接方法,而让规划器决定其他一切,只需传递HASH_JOIN(d)。
强制不同的计划
改变建议字符串以强制不同的计划形态:
SETLOCALpg_plan_advice.advice='MERGE_JOIN_PLAIN(d)';EXPLAIN(COSTSOFF,PLAN_ADVICE)SELECT*FROMjoin_fact fJOINjoin_dim dONf.dim_id=d.id;QUERY PLAN ---------------------------------------------------------------- Merge Join Merge Cond: (f.dim_id = d.id) -> Index Scan using join_fact_dim_id on join_fact f -> Index Scan using join_dim_pkey on join_dim d Supplied Plan Advice: MERGE_JOIN_PLAIN(d) /* matched */ Generated Plan Advice: JOIN_ORDER(f d) MERGE_JOIN_PLAIN(d) INDEX_SCAN(f public.join_fact_dim_id d public.join_dim_pkey) NO_GATHER(f d)注意生成的建议是如何更新以反映新计划的。改变连接方法,扫描方法也随之改变——规划器现在选择索引扫描以为合并连接提供排序输入。建议机制强制执行你指定的选择,而规划器仍然围绕这些选择优化其他所有内容。
建议词汇表
从回归测试和 Haas 的博客文章来看,当前的指令包括:
JOIN_ORDER(rel1 rel2 ...)—— 命名关系的连接顺序HASH_JOIN(rel),MERGE_JOIN_PLAIN(rel)—— 给定关系的连接方法SEQ_SCAN(rel),INDEX_SCAN(rel idx ...)—— 扫描方法,带有可选的索引规范NO_GATHER(rel rel ...)—— 禁用命名关系的并行 gather
这比 Oracle 或pg_hint_plan支持的范围要窄。明显缺失的包括:基数覆盖、行数注入、规划器成本覆盖、返回集合函数的提示、物化指令。其表面积是故意做小的。这是 1.0 版本,Haas 对此很坦诚——“有很多限制”和“一些仍然相当笨拙的东西”。预计词汇表会增长。
pg_stash_advice:不触碰查询的建议
为每个查询设置一个 GUC 对于交互式调试来说没问题,但对于生产环境来说毫无用处。这就是pg_stash_advice的用途:
ALTERSYSTEMSETshared_preload_libraries='pg_stash_advice';ALTERSYSTEMSETpg_stash_advice.stash_name='my_stash';SELECTpg_create_advice_stash('my_stash');SELECTpg_set_stashed_advice('my_stash',:qid,'MERGE_JOIN_PLAIN(d)');重新加载后,每次在pg_stash_advice.stash_name匹配的会话中规划具有该查询标识符的查询时,存储的建议都会自动应用。你没有触碰 SQL。你没有触碰应用程序。你从一个具有适当权限的数据库角色固定了计划。
这是 SQL Server 的计划指南模式和 Db2 的优化配置文件模式,在它们出现近二十年后才落地 PostgreSQL。
存储库通过查询标识符来键——就是你在pg_stat_statements中看到的那个标识符。因此工作流程自然形成:通过pg_stat_statements找到一个慢查询,通过EXPLAIN (PLAN_ADVICE)为你想要的计划获取建议,将其存储到查询 id 下,然后就不用管了。
如果pg_plan_advice.advice和存储的建议条目同时适用于一个查询,则会话级别的设置获胜。存储的建议是基线;会话设置是覆盖。
机制,而非策略
Haas 对该架构的表述很明确:pg_plan_advice是可插拔的核心,而pg_stash_advice和pg_collect_advice是演示可插拔性让你能构建什么的示例扩展。pg_stash_advice通过查询标识符键并在动态共享内存中存储,这是一个合理的默认值,并不是因为它唯一正确的设计。一个不同的扩展可以通过你计算的查询哈希、通过规范化查询文本的正则表达式、通过用户或角色、通过任何方式来键。一个不同的扩展可以将建议存储在目录表、etcd、sidecar 服务中。
这就是为什么pg_plan_advice不是将pg_hint_plan引入核心。pg_hint_plan是一个完整的、有主见的解决方案。pg_plan_advice是一个工具包。
实际的结果是,一旦这个落地,pg_hint_plan本身就可以减少大量的代码。它目前为了实现其功能而复制了规划器的大部分内容;新的基础设施可以让大部分重复代码消失。pg_hint_plan将成为pg_plan_advice钩子的消费者,而不是规划器的并行实现。无论你对提示这一概念持何种看法,这都是一个真正的工程学胜利。
这如何回答(或不回答)六个反对理由
回顾一下第二部分的列表:
- 可维护性。
pg_stash_advice将建议键与查询标识符关联,而不是嵌入在 SQL 中。你在数据库中更改建议,而不是在应用程序中。已回答。 - 升级干扰。部分回答。如果你只存储你关心的特定指令——例如
MERGE_JOIN_PLAIN(d),没有别的——规划器会继续自适应地优化其他所有内容。一个完整的、逐字固定的建议字符串会重新引入这个问题。该工具允许克制使用;它不强制执行。 - 助长 DBA 的坏习惯。未回答,也无意回答。一个求助于
pg_plan_advice而不是修复统计信息问题的 DBA 将会得到与使用pg_hint_plan相同的糟糕结果。这是一个文化问题,而不是技术问题。 - 不随数据规模扩展。部分回答,原因与升级相同。有针对性的建议比完全固定计划更能承受数据增长。完全固定计划有其一直以来的问题。
- 通常是不必要的。无关。这个反对意见从来就不是关于机制应该是什么样子,而只是关于机制是否应该存在。
pg_plan_advice承认它有时是必要的。 - 干扰规划器开发。原始反对意见中最薄弱的一个,现在更弱了。
pg_stat_statements加上pg_plan_advice使得描述和报告规划器衰退变得微不足道。存储的建议是证据,而不是掩饰。
状态
该补丁尚未提交。Haas 指出“将功能纳入 v19 的时间正在迅速耗尽”,并且 -hackers 的讨论一直很热烈——既有支持也有怀疑。Andrey 在博客评论中指出,pg_hint_plan已经解决了这个问题,并且不应为了服务一个扩展已经处理的用例而使核心规划器复杂化。这不是一个边缘的反对意见。这是对 2011 年立场的合理重述,针对 2026 年进行了重申。
无论pg_plan_advice是落地于 19、20 还是永远不会落地,争论已经发生了变化。核心项目不再说“我们不想要这个”。它正在就具体形态进行协商。
这才是真正的新闻。
