Elizabeth Garrett Christensen:PostgreSQL 19 中的查询计划提示:如何以及何时使用建议

Elizabeth Garrett Christensen:PostgreSQL 19 中的查询计划提示:如何以及何时使用建议

💡 原文英文,约1700词,阅读约需6分钟。
📝

内容提要

PostgreSQL 19 将新增查询提示功能,通过 pg_plan_advice 等扩展模块,用户可指定连接顺序、扫描方式和索引,覆盖优化器默认计划。该功能适用于无法修改的查询或函数估算错误场景,但官方提醒优化器通常正确,应优先分析表和调优索引,提示仅按查询 ID 精准使用,并常检查执行计划。

🔎

延伸解读

为何 PostgreSQL 长期拒绝提示

PostgreSQL 社区多年来反对添加查询提示,认为只要正确分析表,优化器就能做出正确选择。优化器基于 pg_statistic 中的统计信息和成本模型,评估多种连接顺序和策略,选择成本最低的计划。糟糕的优化器选择被视为 bug 并会快速修复。因此,在大多数情况下,修复应用 bug、更新统计信息或添加索引是更好的解决方案。

提示的适用场景与风险

提示适用于无法修改的查询代码,如外部应用、外部数据包装器或不透明函数。优化器无法看到 PL/pgSQL 函数、PostGIS 操作或自定义业务逻辑函数的内部,可能做出错误估算。例如,一个合规检查函数仅标记 100 万订单中的 50 个,但优化器假设匹配 33.3 万行,导致全表扫描。使用提示可改为嵌套循环和索引查找,速度提升约 2 倍。但提示会覆盖优化器,若使用不当可能使性能变差,需经常检查执行计划。

按查询 ID 精准使用建议

建议可以按不同级别作用域,但生产环境中推荐仅按查询 ID 使用。查询 ID 基于查询形状、表和连接条件生成,字面值不同不影响 ID,参数化查询也有相同 ID。这与 pg_stat_statements 使用的标识符相同,便于定位慢查询并为其存储建议。通过 pg_stash_advice 扩展,可以创建命名建议集合,并为特定查询 ID 设置建议,避免影响未遇到的查询。

EXPLAIN 生成建议与优雅降级

PostgreSQL 19 的 EXPLAIN 命令可以生成建议字符串,例如 EXPLAIN (COSTS OFF, PLAN_ADVICE) 会输出 Generated Plan Advice,可直接复制并修改后反馈给优化器以锁定计划。如果建议无效,pg_plan_advice 不会导致查询失败,而是优雅降级并在日志中提供详细反馈(需设置 pg_plan_advice.trace_mask=true)。但若建议本身不佳,查询仍会执行,因此需谨慎并经常检查计划。

❓

Q&A

PostgreSQL 19 新增的查询提示功能叫什么?通过哪些扩展模块实现?

PostgreSQL 19 新增的查询提示功能官方称为“advice”(建议),通过两个新的 contrib 扩展模块实现:pg_plan_advice 和 pg_stash_advice。pg_plan_advice 用于直接设置和执行计划建议,pg_stash_advice 用于将建议按查询 ID 保存以便复用。

在 PostgreSQL 19 中如何临时为某条查询指定执行计划建议?

可以通过设置 pg_plan_advice.advice 参数来临时指定建议,例如:SET pg_plan_advice.advice = 'JOIN_ORDER(c o) NESTED_LOOP_MEMOIZE(o) INDEX_SCAN(o idx_orders_customer)'; 然后执行查询,该建议会生效。使用 RESET pg_plan_advice.advice; 可恢复让优化器自行决定。

PostgreSQL 19 的查询建议可以控制哪些执行计划要素?

查询建议可以控制扫描方式(如 SEQ_SCAN、INDEX_SCAN)、连接顺序(JOIN_ORDER)、连接方法(如 HASH_JOIN、NESTED_LOOP_PLAIN、NESTED_LOOP_MEMOIZE)以及并行执行(如 NO_GATHER、GATHER_MERGE)。

什么情况下应该考虑使用 PostgreSQL 19 的查询建议?

当无法修改查询代码(如外部应用、外部数据包装器或不透明函数),且优化器因无法准确估算函数选择性而选择了糟糕的计划时,可以考虑使用查询建议。例如,一个函数实际只匹配 50 行,但优化器误以为匹配 33% 的行,导致全表扫描和哈希连接,此时建议可以强制使用嵌套循环和索引扫描。

使用 PostgreSQL 19 查询建议时有哪些风险和注意事项?

官方提醒优化器通常是对的,错误使用建议可能使性能更差。建议应仅按查询 ID 精准使用,避免影响其他查询;需经常运行 EXPLAIN 检查执行计划,确保建议确实带来改善。如果建议无效或错误,查询不会失败,但可能产生更差的计划。

如何将查询建议保存下来供后续使用?

可以使用 pg_stash_advice 扩展。首先创建命名建议集合,例如 SELECT pg_create_advice_stash('production_fixes'); 然后通过 EXPLAIN VERBOSE 获取查询 ID,最后用 pg_set_stashed_advice 将建议字符串与查询 ID 关联保存,例如 SELECT pg_set_stashed_advice('production_fixes', 9122549731181782750, 'JOIN_ORDER(c o) NESTED_LOOP_MEMOIZE(o) INDEX_SCAN(o idx_orders_customer) NO_GATHER(c o)');

PostgreSQL 19 的 EXPLAIN 命令如何帮助生成查询建议?

使用 EXPLAIN (COSTS OFF, PLAN_ADVICE) 可以在输出中生成“Generated Plan Advice”字符串,该字符串可直接复制并修改后用于设置 pg_plan_advice.advice,从而锁定计划。这是该功能的一大亮点,简化了建议的编写。

🏷️

标签

➡️

继续阅读