PostgreSQL 执行计划中 Seq Scan vs Index Scan 的成本计算

8次阅读

Seq Scan 成本 = startup_cost + (pages × seq_page_cost) + (rows × cpu_tuple_cost),基于统计信息和配置参数估算,不访问真实数据。

PostgreSQL 执行计划中 Seq Scan vs Index Scan 的成本计算

Seq Scan 的成本是怎么算出来的

postgresql 估算 Seq Scan 成本时,核心是「读多少页 + 处理多少行」。它不查真实数据,只依赖统计信息(pg_class.relpagespg_class.reltuples)和配置参数。

基础公式近似为:startup_cost + (pages × seq_page_cost) + (rows × cpu_tuple_cost)。其中:

  • startup_cost 通常为 0(除非带 LIMIT 或排序)
  • seq_page_cost 默认是 1.0,可调;代表随机读一页的开销相对值
  • cpu_tuple_cost 默认是 0.01,反映每行 CPU 处理开销
  • 页数来自 relpages,但若 WHERE 条件有选择率(selectivity),会按比例缩减扫描页数(注意:不是简单乘,还涉及缓冲区命中率估算)

Index Scan 成本为什么经常比 Seq Scan 高

看起来走索引更快,但优化器算得更细——Index Scan 成本 = 索引页访问成本 + 回表(heap fetch)成本。尤其当条件选择率不高、或索引列区分度低时,它很容易输。

关键点:

  • 索引扫描页数由 index_pages × selectivity 估算,但 B-tree 深度也参与计算(深度越大,越倾向放弃)
  • 回表成本占大头:每匹配一个索引项,就要去主表读一页(或几页)取完整行,这部分用 random_page_cost(默认 4.0)计价,远高于 seq_page_cost
  • 如果查询需要返回大量行(比如 WHERE status IN ('a','b','c') 匹配 30% 行),优化器常判断「不如全扫一遍」,直接选 Seq Scan

如何让 Index Scan 成本变低(让优化器愿意用)

不是建了索引就自动变快,得让成本模型「信服」索引更优。常见有效操作:

  • 确保统计信息最新:ANALYZE table_name;(否则 reltuples 错误会导致选择率误判)
  • 调低 random_page_cost(SSD 场景可设为 1.1–1.5),缩小回表惩罚
  • 用覆盖索引避免回表:CREATE INDEX ON t (a) include (b, c);,此时成本中去掉 heap fetch 项
  • 限制结果集:LIMIT 10 会让 Index Scan 的 startup_cost 显著降低(优化器知道不用扫完)

看执行计划时容易忽略的两个细节

成本数字本身是相对值,单位无意义,但差 2 倍以上通常代表路径差异显著。真正要盯的是:

  • Rows Removed by Filter: 如果这个值巨大(比如扫描 100 万行,只留 10 行),说明谓词不适合走索引,或者索引没覆盖过滤条件
  • 实际耗时与成本严重偏离时(如 Index Scan 成本低但执行慢),大概率是缓存未预热(首次运行)、或 work_mem 不足导致排序/聚合落盘,和成本模型无关
text=ZqhQzanResources