Skip to content

Commit 96acc18

Browse files
committed
Add blog: pg_plan_advice query plan control feature (Week 03)
1 parent 07e4a81 commit 96acc18

3 files changed

Lines changed: 330 additions & 0 deletions

File tree

src/SUMMARY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- [2026](./en/2026/README.md)
66
- [Week 03](./en/2026/03/README.md)
77
- [Extended Statistics Import/Export Functions](./en/2026/03/extended-statistics-import-functions.md)
8+
- [pg_plan_advice: Query Plan Control](./en/2026/03/pg-plan-advice.md)
89

910
---
1011

@@ -13,3 +14,4 @@
1314
- [2026](./cn/2026/README.md)
1415
- [第 03 周](./cn/2026/03/README.md)
1516
- [扩展统计信息导入/导出功能](./cn/2026/03/extended-statistics-import-functions.md)
17+
- [pg_plan_advice:查询计划控制](./cn/2026/03/pg-plan-advice.md)

src/cn/2026/03/pg-plan-advice.md

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
# pg_plan_advice:PostgreSQL 查询计划控制的新方案
2+
3+
## 背景介绍
4+
5+
PostgreSQL 的查询优化器功能强大,通常能生成优秀的执行计划。然而,经验丰富的 DBA 和开发者偶尔会遇到需要影响或稳定优化器决策的场景。来自 EnterpriseDB 的 Robert Haas 正在开发一个重要的 contrib 模块 `pg_plan_advice`,旨在解决这一长期存在的需求。
6+
7+
本文分析了 pgsql-hackers 邮件列表上自 2025 年 10 月以来持续讨论的 [pg_plan_advice 线程](https://www.postgresql.org/message-id/flat/CA%2BTgmoZ-Jh1T6QyWoCODMVQdhTUPYkaZjWztzP1En4%3DZHoKPzw%40mail.gmail.com)
8+
9+
## pg_plan_advice 是什么?
10+
11+
`pg_plan_advice` 是一个提议中的 contrib 模块,引入了一种专门用于控制关键规划决策的"建议迷你语言"(advice mini-language)。该模块支持:
12+
13+
- 使用 `EXPLAIN (PLAN_ADVICE)` 从现有查询计划**生成建议字符串**
14+
- 通过 `pg_plan_advice.advice` GUC 参数**应用建议字符串**,以复现或约束后续的规划决策
15+
16+
建议语言允许控制:
17+
18+
- **连接顺序**:表的连接顺序
19+
- **连接方法**:Nested Loop、Merge Join、Hash Join
20+
- **扫描类型**:顺序扫描、索引扫描(可指定具体索引)
21+
- **并行执行**:并行执行的位置和方式
22+
- **分区连接**:分区表连接的处理方式
23+
24+
## 核心设计理念
25+
26+
Robert Haas 在 README 中强调,主要使用场景并非让用户"战胜优化器",而是**复现过去表现良好的执行计划**
27+
28+
> "我们不需要接受用户能比优化器做出更好规划的观点。我们只需要接受用户比优化器更能分辨好计划和坏计划。这是一个很低的门槛。优化器永远不知道它生成的计划实际执行时会发生什么,但用户知道。"
29+
30+
这将 `pg_plan_advice` 定位为**计划稳定性工具**,而非微观管理优化器的提示系统。
31+
32+
## 技术架构
33+
34+
### 关系标识符系统
35+
36+
`pg_plan_advice` 最创新的方面之一是其关系标识符系统(Relation Identifier System)。该系统提供对查询各部分的**无歧义引用**,能处理复杂场景:
37+
38+
- 同一表使用不同别名的多次引用
39+
- 子查询和 CTE
40+
- 分区表及其分区
41+
42+
标识符语法使用特殊表示法如 `t#2` 来区分查询中表 `t` 的第一次和第二次出现。
43+
44+
### 使用示例
45+
46+
以下是 Jakub Wartak 测试中展示的系统能力:
47+
48+
```sql
49+
-- 为带别名的查询生成建议
50+
EXPLAIN (PLAN_ADVICE, COSTS OFF)
51+
SELECT * FROM (SELECT * FROM t1 a JOIN t2 b USING (id)) a, t2 b, t3 c
52+
WHERE a.id = b.id AND b.id = c.id;
53+
54+
-- 输出包含:
55+
-- Generated Plan Advice:
56+
-- JOIN_ORDER(a#2 b#2 c)
57+
-- MERGE_JOIN_PLAIN(b#2 c)
58+
-- SEQ_SCAN(c)
59+
-- INDEX_SCAN(a#2 public.t1_pkey)
60+
-- NO_GATHER(c a#2 b#2)
61+
```
62+
63+
然后可以选择性地应用约束:
64+
65+
```sql
66+
-- 强制使用特定扫描类型
67+
SET pg_plan_advice.advice = 'SEQ_SCAN(b#2)';
68+
69+
-- 重新执行 EXPLAIN 查看新计划
70+
EXPLAIN (PLAN_ADVICE, COSTS OFF)
71+
SELECT * FROM (SELECT * FROM t1 a JOIN t2 b USING (id)) a, t2 b, t3 c
72+
WHERE a.id = b.id AND b.id = c.id;
73+
74+
-- 输出显示:
75+
-- Supplied Plan Advice:
76+
-- SEQ_SCAN(b#2) /* matched */
77+
```
78+
79+
## 补丁结构(v10)
80+
81+
实现分为五个补丁:
82+
83+
| 补丁 | 描述 | 大小 |
84+
|------|------|------|
85+
| 0001 | 存储范围表扁平化信息 | 7.8 KB |
86+
| 0002 | 在最终计划中存储省略节点的信息 | 9.8 KB |
87+
| 0003 | 存储 Append 节点合并信息 | 40.4 KB |
88+
| 0004 | 允许插件控制路径生成策略 | 56.1 KB |
89+
| 0005 | WIP:添加 pg_plan_advice contrib 模块 | 399.1 KB |
90+
91+
前四个补丁为优化器添加必要的基础设施,第五个包含实际模块。这种分离设计使基础设施在未来可能惠及其他扩展。
92+
93+
## 社区审查与测试
94+
95+
该线程得到了多位社区成员的积极参与:
96+
97+
### Jakub Wartak(EDB)
98+
进行了大量 TPC-H 基准测试,发现了若干 bug:
99+
- debug/ASAN 构建中的空指针崩溃
100+
- 无统计信息时的半连接唯一性检测问题
101+
- 复杂查询中的连接顺序建议冲突
102+
103+
### Jacob Champion(EDB)
104+
应用模糊测试技术发现边缘情况:
105+
- 畸形建议字符串导致的解析器崩溃
106+
- 对非分区表使用分区相关建议的问题
107+
- 通过语料库模糊测试发现的 AST 工具 bug
108+
109+
### 其他贡献者
110+
- **Alastair Turner**:赞赏测试替代计划的能力
111+
- **Hannu Krosing**(Google):引用 VLDB 研究,显示 20% 的实际查询有 10+ 个连接
112+
- **Lukas Fittl**:对与 pg_stat_statements 集成的可能性感兴趣
113+
114+
## 发现并修复的问题
115+
116+
协作审查过程发现并修复了多个版本中的若干问题:
117+
118+
1. **编译器警告**(gcc-13、clang-20)- 在早期版本中修复
119+
2. 扩展状态未分配时 `pgpa_join_path_setup()` 中的**空指针崩溃**
120+
3. **连接顺序冲突检测**错误地将连接方法建议视为正向约束
121+
4. 在 EXPLAIN 中未使用 PLAN_ADVICE 时**半连接唯一性追踪**工作不正确
122+
5. 嵌套连接顺序规范中的**部分匹配检测**问题
123+
124+
## 当前状态
125+
126+
截至 v10(2026 年 1 月 15 日发布):
127+
128+
- 补丁已注册在 [Commitfest](https://commitfest.postgresql.org/patch/6184/)
129+
- 仍标记为 **WIP(进行中)**
130+
- 测试仍在进行,特别是 TPC-H 查询测试
131+
- Robert Haas 正在寻求实质性的代码审查,特别是针对补丁 0001
132+
133+
## 对 PostgreSQL 用户的意义
134+
135+
如果被合并,`pg_plan_advice` 将提供:
136+
137+
1. **计划稳定性**:捕获并复现已知良好的查询计划
138+
2. **调试辅助**:理解优化器为何做出特定选择
139+
3. **测试工具**:在不修改查询的情况下实验替代计划形状
140+
4. **生产安全网**:防止统计信息变化后的意外计划退化
141+
142+
## 与 pg_hint_plan 的比较
143+
144+
与流行的 `pg_hint_plan` 扩展不同,`pg_plan_advice` 专注于**往返安全性(round-trip safety)**
145+
146+
- 计划可以被可靠地捕获和重新应用
147+
- 关系标识符系统自动处理复杂的别名
148+
- 设计为可与任何查询结构配合使用,无需手动管理标识符
149+
150+
## 总结
151+
152+
`pg_plan_advice` 代表了 PostgreSQL 优化器可扩展性方面的重要进步。它不是要取代优化器的判断,而是提供一种安全机制来保留经过验证的执行策略。活跃的社区审查过程已经大幅改进了代码,持续的测试正在帮助确保其健壮性。
153+
154+
对于管理复杂工作负载的 DBA,特别是那些查询偶尔遭受计划退化的场景,该模块提供了一个有前途的解决方案,它与优化器**协同工作**而非对抗。
155+
156+
---
157+
158+
**邮件列表链接**: [pg_plan_advice - pgsql-hackers](https://www.postgresql.org/message-id/flat/CA%2BTgmoZ-Jh1T6QyWoCODMVQdhTUPYkaZjWztzP1En4%3DZHoKPzw%40mail.gmail.com)
159+
160+
**Commitfest 条目**: [CF 6184](https://commitfest.postgresql.org/patch/6184/)
161+
162+
**作者**: Robert Haas(EnterpriseDB)
163+
164+
**审查者**: Jakub Wartak、Jacob Champion、Alastair Turner、Hannu Krosing、John Naylor 等

src/en/2026/03/pg-plan-advice.md

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
# pg_plan_advice: A New Approach to PostgreSQL Query Plan Control
2+
3+
## Introduction
4+
5+
PostgreSQL's query planner is sophisticated and generally produces excellent execution plans. However, experienced DBAs and developers occasionally encounter situations where they wish they could influence or stabilize the planner's decisions. Robert Haas from EnterpriseDB has been working on a significant new contrib module called `pg_plan_advice` that aims to address this long-standing need.
6+
7+
This article examines the [pg_plan_advice thread](https://www.postgresql.org/message-id/flat/CA%2BTgmoZ-Jh1T6QyWoCODMVQdhTUPYkaZjWztzP1En4%3DZHoKPzw%40mail.gmail.com) on the pgsql-hackers mailing list, which has been actively discussed since October 2025.
8+
9+
## What is pg_plan_advice?
10+
11+
`pg_plan_advice` is a proposed contrib module that introduces a special-purpose "advice mini-language" for controlling key planning decisions. The module can:
12+
13+
- **Generate advice strings** from existing query plans using `EXPLAIN (PLAN_ADVICE)`
14+
- **Apply advice strings** via the `pg_plan_advice.advice` GUC parameter to reproduce or constrain future planning decisions
15+
16+
The advice language allows control over:
17+
18+
- **Join order**: Which tables are joined in what sequence
19+
- **Join methods**: Nested loop, merge join, hash join
20+
- **Scan types**: Sequential scan, index scan (with specific index selection)
21+
- **Parallelism**: Where and how parallel execution is used
22+
- **Partitionwise joins**: How partitioned table joins are handled
23+
24+
## Key Design Philosophy
25+
26+
Robert Haas emphasizes in the README that the principal use case is not about users "out-planning the planner" but rather about **reproducing plans that worked well in the past**:
27+
28+
> "We don't need to accept the proposition that users can out-plan the planner. We only need to accept that they can tell good plans from bad plans better than the planner. That is a low bar to clear. The planner never finds out what happens when the plans that it generates are actually executed, but users do."
29+
30+
This positions `pg_plan_advice` as a **plan stability tool** rather than a hint system for micromanaging the optimizer.
31+
32+
## Technical Architecture
33+
34+
### The Relation Identifier System
35+
36+
One of the most innovative aspects of `pg_plan_advice` is its relation identifier system. This system provides **unambiguous references** to parts of a query, handling complex scenarios like:
37+
38+
- Multiple references to the same table with different aliases
39+
- Subqueries and CTEs
40+
- Partitioned tables and their partitions
41+
42+
The identifier syntax uses special notation like `t#2` to distinguish between the first and second occurrence of table `t` in a query.
43+
44+
### Example Usage
45+
46+
Here's an example from Jakub Wartak's testing showing the power of the system:
47+
48+
```sql
49+
-- Generate advice for a query with aliasing
50+
EXPLAIN (PLAN_ADVICE, COSTS OFF)
51+
SELECT * FROM (SELECT * FROM t1 a JOIN t2 b USING (id)) a, t2 b, t3 c
52+
WHERE a.id = b.id AND b.id = c.id;
53+
54+
-- Output includes:
55+
-- Generated Plan Advice:
56+
-- JOIN_ORDER(a#2 b#2 c)
57+
-- MERGE_JOIN_PLAIN(b#2 c)
58+
-- SEQ_SCAN(c)
59+
-- INDEX_SCAN(a#2 public.t1_pkey)
60+
-- NO_GATHER(c a#2 b#2)
61+
```
62+
63+
You can then selectively apply constraints:
64+
65+
```sql
66+
-- Force a specific scan type
67+
SET pg_plan_advice.advice = 'SEQ_SCAN(b#2)';
68+
69+
-- Re-explain to see the new plan
70+
EXPLAIN (PLAN_ADVICE, COSTS OFF)
71+
SELECT * FROM (SELECT * FROM t1 a JOIN t2 b USING (id)) a, t2 b, t3 c
72+
WHERE a.id = b.id AND b.id = c.id;
73+
74+
-- The output shows:
75+
-- Supplied Plan Advice:
76+
-- SEQ_SCAN(b#2) /* matched */
77+
```
78+
79+
## Patch Structure (v10)
80+
81+
The implementation is split into five patches:
82+
83+
| Patch | Description | Size |
84+
|-------|-------------|------|
85+
| 0001 | Store information about range table flattening | 7.8 KB |
86+
| 0002 | Store information about elided nodes in the final plan | 9.8 KB |
87+
| 0003 | Store information about Append node consolidation | 40.4 KB |
88+
| 0004 | Allow for plugin control over path generation strategies | 56.1 KB |
89+
| 0005 | WIP: Add pg_plan_advice contrib module | 399.1 KB |
90+
91+
The first four patches add necessary infrastructure to the planner, while the fifth contains the actual module. This separation allows the infrastructure to potentially benefit other extensions in the future.
92+
93+
## Community Review and Testing
94+
95+
The thread has seen active participation from several community members:
96+
97+
### Jakub Wartak (EDB)
98+
Conducted extensive TPC-H benchmark testing and found several bugs:
99+
- Crashes in debug/ASAN builds with NULL pointer dereferences
100+
- Issues with semijoin uniqueness detection without statistics
101+
- Join order advice conflicts in complex queries
102+
103+
### Jacob Champion (EDB)
104+
Applied fuzzing techniques to discover edge cases:
105+
- Parser crashes with malformed advice strings
106+
- Issues with partition-related advice on non-partitioned tables
107+
- AST utility bugs revealed through corpus-based fuzzing
108+
109+
### Other Contributors
110+
- **Alastair Turner**: Appreciated the ability to test alternative plans
111+
- **Hannu Krosing** (Google): Referenced VLDB research showing 20% of real-world queries have 10+ joins
112+
- **Lukas Fittl**: Interested in pg_stat_statements integration possibilities
113+
114+
## Issues Discovered and Fixed
115+
116+
The collaborative review process has uncovered and fixed several issues across versions:
117+
118+
1. **Compiler warnings** (gcc-13, clang-20) - Fixed in early versions
119+
2. **NULL pointer crashes** in `pgpa_join_path_setup()` when extension state wasn't allocated
120+
3. **Join order conflict detection** incorrectly treating join method advice as positive constraints
121+
4. **Semijoin uniqueness tracking** not working correctly without PLAN_ADVICE in EXPLAIN
122+
5. **Partial match detection** in nested join order specifications
123+
124+
## Current Status
125+
126+
As of v10 (posted January 15, 2026):
127+
128+
- The patch is registered in [Commitfest](https://commitfest.postgresql.org/patch/6184/)
129+
- Still marked as **WIP (Work In Progress)**
130+
- Active testing continues, particularly with TPC-H queries
131+
- Robert Haas is seeking substantive code review, especially for patch 0001
132+
133+
## Implications for PostgreSQL Users
134+
135+
If committed, `pg_plan_advice` would provide:
136+
137+
1. **Plan Stability**: Capture and reproduce known-good query plans
138+
2. **Debugging Aid**: Understand why the planner makes specific choices
139+
3. **Testing Tool**: Experiment with alternative plan shapes without modifying queries
140+
4. **Production Safety Net**: Guard against unexpected plan regressions after statistics changes
141+
142+
## Comparison with pg_hint_plan
143+
144+
Unlike the popular `pg_hint_plan` extension, `pg_plan_advice` focuses on **round-trip safety**:
145+
146+
- Plans can be captured and reapplied reliably
147+
- The relation identifier system handles complex aliasing automatically
148+
- Designed to work with any query structure without manual identifier management
149+
150+
## Conclusion
151+
152+
`pg_plan_advice` represents a significant step forward in PostgreSQL's planner extensibility story. Rather than replacing the optimizer's judgment, it provides a safety mechanism for preserving proven execution strategies. The active community review process has already improved the code substantially, and continued testing is helping ensure robustness.
153+
154+
For DBAs managing complex workloads, particularly those with queries that occasionally suffer from plan regressions, this module offers a promising solution that works *with* the planner rather than against it.
155+
156+
---
157+
158+
**Thread Link**: [pg_plan_advice - pgsql-hackers](https://www.postgresql.org/message-id/flat/CA%2BTgmoZ-Jh1T6QyWoCODMVQdhTUPYkaZjWztzP1En4%3DZHoKPzw%40mail.gmail.com)
159+
160+
**Commitfest Entry**: [CF 6184](https://commitfest.postgresql.org/patch/6184/)
161+
162+
**Author**: Robert Haas (EnterpriseDB)
163+
164+
**Reviewers**: Jakub Wartak, Jacob Champion, Alastair Turner, Hannu Krosing, John Naylor, and others

0 commit comments

Comments
 (0)