PostgreSQL 19: 提供查询计划干预扩展 pg_plan_advice

John Doe 三月 18, 2026

在 PostgreSQL 的业务运维中,执行计划回退是最让 DBA 头疼的问题之一:一条长期稳定运行的核心 SQL,可能因为统计信息更新、数据库版本升级、配置参数调整,突然被查询规划器选择了错误的执行计划,导致性能从毫秒级暴跌至秒级甚至分钟级,严重影响业务稳定性。

image

此前,用户只能通过第三方插件pg_hint_plan、重写 SQL、调整全局规划参数等方式临时解决,不仅门槛高、兼容性风险大,还缺乏官方原生的稳定支持。PostgreSQL 本次提交正式新增了官方 contrib 模块pg_plan_advice,提供了原生的执行计划锁定与强制干预能力,从内核层面填补了计划稳定性的核心缺口。

特性提交日志

新增 contrib 模块 pg_plan_advice。

提供以下功能:

(1) 可用于稳定特定的计划选择,确保规划器在未经授权时无法更改决策路径;

(2) 可供专业用户使用,以强制采纳与规划器最优判断不符的计划选择。

两种场景下均可能产生不良后果:用户在限制规划器自主决策前应再三斟酌;然而,有些问题借助这些功能可显著降低解决难度,远胜于不用。

本次提交采用分析已生成计划的方式,输出我们称之为 “计划建议” 的文本描述,该描述详细记录计划生成过程中的关键决策;若在后续规划周期中提供此计划建议,将强制规划器以相同方式做出这些关键决策。并非所有规划器决策都可通过建议控制,例如,聚合执行方式及排序顺序的选择当前不在此列。用户可编辑计划建议,或在简单场景下直接从零编写,从而实现规划器原生无法生成的结果。也可提供部分建议,以控制部分规划器结果而非全部。

目前,计划建议仅聚焦于特定结果,例如对特定关系选择顺序扫描,而非可能影响这些结果的估算(如可能不准确的选择率估算)。尽管支持影响选择率估算或成本计算等其他方面的计划建议对用户颇具价值,但本次提交暂不包含此功能。

讨论:http://postgr.es/m/CA+TgmoZ-Jh1T6QyWoCODMVQdhTUPYkaZjWztzP1En4=ZHoKPzw@mail.gmail.com

特性示例

扩展加载完成后,EXPLAIN 命令将支持PLAN_ADVICE选项,通过该选项可查看当前查询生成的执行计划对应的建议字符串,示例如下:

EXPLAIN (COSTS OFF, PLAN_ADVICE)
        SELECT * FROM join_fact f JOIN join_dim d ON f.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)

本例中用户未指定任何计划建议,优化器自主做出了最优决策,并将这些决策以建议字符串的形式记录,各字符串含义如下:

  • JOIN_ORDER (f d):以 f 表为驱动表,首个关联的表为 d 表;
  • HASH_JOIN (d):d 表作为哈希连接的内表;
  • SEQ_SCAN (f d):对 f 表和 d 表均采用顺序扫描的方式;
  • NO_GATHER (f d):f 表和 d 表均不置于 Gather 或 Gather Merge 节点之下。

关于计划建议微型语言的更多细节,可参考官方文档中的相关内容。

获取查询的建议字符串后,可通过设置配置项pg_plan_advice.advice为目标建议字符串,来控制该查询的执行计划生成。该配置项的值可以是系统自动生成的建议字符串,也可以是用户自定义的内容。自定义建议字符串的一个实用方法是:截取系统生成的字符串,仅保留需要强制生效的部分。例如,仅需控制表的连接顺序时,可按如下方式操作:

SET pg_plan_advice.advice = 'JOIN_ORDER(f d)';

EXPLAIN (COSTS OFF)
        SELECT * FROM join_fact f JOIN join_dim d ON f.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
 Supplied Plan Advice:
   JOIN_ORDER(f d) /* matched */

由于本例中未指定 EXPLAIN 的PLAN_ADVICE选项,系统不会为该执行计划生成新的建议字符串,但仍会展示已提供的计划建议,让查看 EXPLAIN 结果的人员知晓当前执行计划受计划建议影响。若不需要展示该信息,可将配置项pg_plan_advice.always_explain_supplied_advice设置为 false。

每条已提供的计划建议后,都会附带建议反馈,用于说明该建议是否成功应用到查询中。本例中的/* matched */表示,查询中存在 f 表和 d 表,且最终生成的执行计划符合该建议的要求。

非常不错的体验,感谢所有参与的社区人员。

参考

提交日志:https://git.postgresql.org/pg/commitdiff/5883ff30b02ceed3c5eabba4d9c09a7766f9a8fc