Skip to content

优化器与执行计划

PostgreSQL 使用基于代价的优化器。它根据统计信息估算各执行节点的行数和成本,再从可能的扫描、连接、聚合与并行方案中选择总代价较低的计划。成本是相对估值,不等于真实毫秒数。


优化链路

估算误差会沿计划树向上传播。底层扫描估错十倍,可能导致连接顺序、连接算法、并行度和内存策略全部发生偏差,因此优化应先找最早出现明显估算偏差的节点。


扫描方式

节点访问方式常见适用条件
Seq Scan顺序读取 Heap 页面并过滤需要大量行、小表或索引选择性低
Index Scan通过索引定位并访问 Heap返回较少行,随机访问成本可接受
Index Only Scan从索引取列,尽量通过 VM 跳过 Heap覆盖查询且页面大多 all-visible
Bitmap Index/Heap Scan汇总 TID 后按 Heap Page 批量访问返回中等数量行或组合多个索引

看到 Seq Scan 不应直接得出“缺索引”。如果查询读取大部分表数据,顺序 I/O 可能比大量索引随机访问更便宜。


连接方式

方式原理常见条件
Nested Loop外侧每行驱动内侧查询外侧结果小,内侧有高选择性索引
Hash Join为一侧建立哈希表,另一侧探测等值连接、输入较大且哈希可控
Merge Join两侧按连接键有序后归并大量有序数据、范围或等值连接

Hash 或 Sort 超过可用工作内存时会写临时文件。work_mem 是每个执行节点的潜在预算,一个查询可同时存在多个 Sort 或 Hash,并发查询也会叠加,不能简单按总内存除以连接数后设置为很大值。


统计信息

ANALYZE 采样形成列分布统计,常见信息包括空值比例、Distinct 估算、Most Common Values、Histogram 和物理相关性。默认按单列统计,列之间存在强相关时,可使用 Extended Statistics 描述多列依赖、联合 Distinct 或常见值组合。

常见估算偏差来源:

  • 统计信息过期或采样目标不足。
  • 数据倾斜严重,常见值未被统计捕捉。
  • 多列高度相关但优化器按独立概率估算。
  • 表达式或类型转换使现有统计和索引无法匹配。
  • 参数化查询使用通用计划,不适合某些参数分布。

阅读 EXPLAIN

EXPLAIN 显示估算计划;EXPLAIN ANALYZE 会真实执行语句,并显示实际行数和时间。对写语句执行 ANALYZE 会实际修改数据,诊断时应放在可回滚事务或安全环境中。

阅读顺序通常是:

  1. 从执行时间和数据量最大的节点定位主要成本。
  2. 比较 Estimated Rows 与 Actual Rows,找最早的数量级偏差。
  3. 结合 Buffers 判断命中缓存、物理读取和脏页写入。
  4. 检查 Filter Removed、Heap Fetches、Sort Spill 和循环次数。
  5. 再决定更新统计、调整 SQL、建立索引或修改数据模型。

不应长期通过关闭某种扫描或连接方式强迫优化器选计划。这类参数适合诊断假设,稳定修复应落在统计、索引、查询结构和数据分布上。

参考资料