Postgres 16 查询规划器有什么新变化

PostgreSQL 16 的查询规划器改进引发了人们对数据库如何选择和执行查询计划的重新审视,尤其是像 nested loop joins 这类更高风险的选择,以及不完整统计信息带来的影响。贡献者们权衡了长期以来关于是否加入查询提示或计划“冻结”作为优化器选出病态计划时的逃生口的争论,并探讨了更好的统计信息、来自执行过程的规划器反馈,以及用于可视化和调优 EXPLAIN 计划的工具等替代方案。他们还指出了现实中的痛点——JIT 编译开销、缺乏共享计划缓存,以及与 SQL Server 等系统的差异——同时总体上认为 PostgreSQL 能处理大规模工作负载,但仍有空间变得更可预测、并能自我修正。

查询提示 vs. 规划器纯粹主义

  • 一个反复出现的重大争论:Postgres 是否应该支持查询提示(query hints)?
  • 支持提示的理由:
    • 当规划器在生产环境中选出糟糕计划时,提示是关键的“逃生口”。
    • 对于快速缓解问题、验证更好的计划,以及在不同版本之间保持一致性能,都很有用。
    • 还希望有带外提示(按 queryid、存储的 outlines),甚至“冻结这个计划”的能力。
  • 反对意见 / 担忧:
    • 随着数据和版本变化,提示会失效,导致把坏计划锁死。
    • 它们会助长糟糕的 DBA 习惯,并损害可维护性。
    • Postgres 文化更倾向于改进规划器本身,并使用更丰富的统计信息,而不是硬性提示。
  • 折中思路:使用更柔和的提示来影响选择率估计或风险容忍度,而不是强制特定的连接类型。

统计信息、选择率与计划风险

  • 许多缓慢的计划都源于错误的行数/选择率估计(例如,假设只有 1 行,于是选择 nested loops,结果实际返回很多行)。
  • 讨论了扩展统计信息、列相关性,以及当前的启发式方法(把独立概率相乘)。
  • 大家希望:
    • 在估计不确定时,规划更“风险规避”。
    • 能表达数据形态知识(单调时间序列、预期表大小)。
    • 让规划器从执行结果获得反馈,并且随着时间推移,可能“学习”哪些计划不好。
    • 对重型 OLAP 查询进行更长时间 / 多轮次的规划。

JIT 编译

  • 一些用户报告说 JIT 会让查询明显变慢,尤其是大量连接或分区很多的查询。
  • 何时启用 JIT 的启发式规则被认为较弱;有些人会全局关闭 JIT,尤其是在 OLTP 场景中。
  • 目前的 JIT 代码不会被缓存;有人提到正在推进缓存能力和改进成本建模的工作。
  • 总体感觉:并行查询通常稳定有帮助;JIT 很强大,但作为默认功能也有风险。

计划检查与工具

  • 可视化工具(如 explain visualizers、pev2、pgMustard 等)很受欢迎。
  • 但是,要判断一个计划是否“糟糕”以及如何修复,仍然需要对 joins、indexes 和 statistics 有深入理解。
  • EXPLAIN ANALYZE 被强调为必不可少;估计行数与实际行数之间的大偏差是关键警示信号。

Postgres vs. 其他数据库

  • 一些人认为 MSSQL/Oracle 的优化器、计划缓存、提示,以及周边生态(jobs、reporting、messaging)更成熟。
  • 也有人指出,Postgres 在实践中扩展性很好;差异更多在功能和工具,而不是原始能力。