来源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选项运行EXPLAINLOADpg_plan_advice;EXPLAIN(COSTSOFF,PLAN_ADVICE)SELECT*FROMjoin_fact fJOINjoin_dim dONf.dim_idd.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.adviceHASH_JOIN(d);EXPLAIN(COSTSOFF,PLAN_ADVICE)SELECT*FROMjoin_fact fJOINjoin_dim dONf.dim_idd.id;规划器产生相同的哈希连接计划并且EXPLAIN输出现在在生成的建议旁边包含一个“Supplied Plan Advice”部分用/* matched */或未匹配注释每个提供的指令。你可以准确地看到规划器采纳了你的哪些建议。你不必传回完整的建议字符串。只关心你关心的部分也可以。如果你只想固定连接方法而让规划器决定其他一切只需传递HASH_JOIN(d)。强制不同的计划改变建议字符串以强制不同的计划形态SETLOCALpg_plan_advice.adviceMERGE_JOIN_PLAIN(d);EXPLAIN(COSTSOFF,PLAN_ADVICE)SELECT*FROMjoin_fact fJOINjoin_dim dONf.dim_idd.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_librariespg_stash_advice;ALTERSYSTEMSETpg_stash_advice.stash_namemy_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 还是永远不会落地争论已经发生了变化。核心项目不再说“我们不想要这个”。它正在就具体形态进行协商。这才是真正的新闻。