PostgreSQL 教程: EXPLAIN 中的行数估算

九月 10, 2025

摘要:在本教程中,您将学习关于 EXPLAIN 输出中的估算行数的意义。

目录

为什么查询规划器需要进行估算?

当 PostgreSQL 为查询制定执行计划时,需要在多种实现方案中做出选择。其中部分选择取决于查询将要处理的数据量,例如,PostgreSQL 提供了多种连接算法,而影响“哪种算法最快”的一个关键因素,就是待连接数据的规模。

若考虑到 PostgreSQL 所有可用的操作类型和数据结构,规划器的选择会更多。例如,EXPLAIN命令的文档中详细提到:对于相同的数据,PostgreSQL 可能会根据数据的用途和所需数据量,采用不同的读取方式。因此,做出准确的行数估算,是查询规划器的一个核心工作。

查询规划器如何进行估算?

在某些情况下,估算过程非常简单,例如,若操作需要读取整个表,那么输出行数就等于表的总行数。但当通过WHERE子句添加筛选条件后,估算会变得复杂。比如,表中有多少行满足WHERE cost > 10的条件?又有多少行满足WHERE f1 @> polygon '(0.5,2.0)'(判断几何对象包含关系)的条件?这类估算往往难度较大!

每当执行ANALYZE命令,或执行带有ANALYZE选项的VACUUM命令(需要注意:ANALYZEEXPLAIN ANALYZE中的ANALYZE完全不同,二者仅名称相同)时,PostgreSQL 会收集表内容的相关统计信息。默认启用的自动清理守护进程(autovacuum)会自动触发这一过程。

这些统计信息包括列中的最常见值、值的分布情况等,它们是通过对表行进行抽样计算得出的,并存储在数据库中(如果有兴趣的话,可以查看pg_stats系统视图)。当查询规划器需要计算“有多少行数据能通过某个筛选条件”时,就会用到这些统计信息。

估算不准确会导致什么问题?

如上面所述,查询规划器会根据行数估算,在性能差异极大的不同查询方案中做选择。若估算结果与实际情况偏差过大,规划器可能会做出错误选择,导致查询执行速度大幅变慢。

幸运的是,判断规划器的行数估算是否准确并不困难,只需查看查询计划即可。对于每个操作节点,计划中都会列出“估算行数(Plan Rows)”和“实际行数(Actual Rows)”,示例如下:

[
  {
    "Plan": {
      "Node Type": "Seq Scan",
      "Plan Rows": 208,
      "Actual Rows": 40154,
      ...
    },
  ...
  }
]

估算不准确并非总会导致严重问题,规划器仍有可能选择到较优的执行计划。不过,若行数估算偏差极大,且你无法解释查询为何执行缓慢,那么行数估算问题值得深入排查。

不应如何修复执行计划?

有时会看到有人建议“手动覆盖查询规划器的选择”,以获得期望的执行计划。例如,强制规划器使用索引扫描,而非其原本选择的顺序扫描。

虽然通过显式指定执行计划的某些部分,可能在短期内提升性能,但这会导致代码变得脆弱。一方面,当数据结构或数据量发生变化时,规划器将难以调整执行计划;另一方面,未来 PostgreSQL 版本推出性能优化特性时,代码也可能无法利用这些新特性。

这里并非完全否定“为实现最优性能而限制规划器选择”的必要性,但总体而言,通过提升统计信息质量来引导规划器做出正确决策,是更稳健的方案。PostgreSQL 的查询规划器设计精细且逻辑严谨,对查询性能的细节有着深刻理解,只要获得足够准确的信息,规划器通常能做出正确选择。

如何帮助查询规划器提升估算准确性?

要改善规划器的统计信息(进而提升估算准确性),首先可以尝试执行VACUUM ANALYZE命令。若只需针对特定表(如table_1),可执行VACUUM (ANALYZE) table_1;若需针对所有表,则执行VACUUM (ANALYZE)

该命令会完成两项工作:清理已删除的数据(VACUUM的功能),以及重新计算统计信息(ANALYZE的功能)。若统计信息因近期的数据写入或删除而过期,更新后的统计信息很可能会更准确。

若重新计算统计信息后,估算效果仍未改善,可尝试增加统计信息的收集量,更详细的统计信息通常能带来更准确的估算结果。

若需对所有表生效,可将default_statistics_target配置参数从默认值 100 调大;若只需针对特定列,可使用ALTER命令,示例如下:

ALTER TABLE table_1 ALTER column_2 SET STATISTICS 400;

多变量统计信息

PostgreSQL 10 还引入了“多变量统计信息”(multivariate statistics)。在默认情况下,行数估算会假设各列的值相互独立,但实际场景中,列之间往往存在关联。例如,假设有如下查询:

SELECT * FROM people
WHERE vegetarian = true AND favourite_dip = 'potato';

假设人群中 14% 是素食者(vegetarian = true),25% 喜爱吃土豆(favourite_dip = 'potato'),那么查询规划器会估算“素食且喜爱吃土豆的人”占比为 14% × 25% = 3.5%。但实际上,素食者喜爱吃土豆的比例可能远高于普通人群,导致实际行数远高于估算值。

多变量统计信息正是为解决这类问题而设计的。它会针对多列组合收集统计信息,让 PostgreSQL 能够理解列之间的相关性或关联关系。

需要注意的是,多变量统计信息不会自动计算。若你怀疑查询计划估算不准确是因列之间的关联导致,可参考 PostgreSQL 官方文档,了解如何创建多变量统计信息

了解更多

PostgreSQL 优化