为啥 PostgreSQL 对索引列执行顺序扫描?

Posted

技术标签:

【中文标题】为啥 PostgreSQL 对索引列执行顺序扫描?【英文标题】:Why does PostgreSQL perform sequential scan on indexed column?为什么 PostgreSQL 对索引列执行顺序扫描? 【发布时间】:2011-07-09 09:25:58 【问题描述】:

非常简单的例子——一张表,一张索引,一次查询:

CREATE TABLE book
(
  id bigserial NOT NULL,
  "year" integer,
  -- other columns...
);

CREATE INDEX book_year_idx ON book (year)

EXPLAIN
 SELECT *
   FROM book b
  WHERE b.year > 2009

给我:

Seq Scan on book b  (cost=0.00..25663.80 rows=105425 width=622)
  Filter: (year > 2009)

为什么它不执行索引扫描? 我错过了什么?

【问题讨论】:

【参考方案1】:

如果 SELECT 返回超过表中所有行的大约 5-10%,则顺序扫描比索引扫描快得多。

这是因为索引扫描需要对每一行进行几个 IO 操作(在索引中查找该行,然后从堆中检索该行)。而顺序扫描每行只需要一个 IO - 甚至更少,因为磁盘上的一个块(页)包含多行,因此单个 IO 操作可以获取多行。

顺便说一句:对于其他 DBMS 也是如此 - 将一些优化作为“仅索引扫描”放在一边(但对于 SELECT *,这样的 DBMS 极不可能进行“仅索引扫描”)

【讨论】:

@Frank:这就是我说“大约”的原因 :) 但是感谢您指出 有趣,这对我来说解释了很多事情 :) 事实上,当我选择年份 > 2010 时,它会进行索引扫描。谢谢! 此外,顺序扫描可以一次从堆中请求多个页面,并要求内核在处理当前块时获取下一个块 - 索引扫描一次获取一个页面. (位图扫描在两者之间进行了折衷,您通常会看到它出现在查询的计划中,这些查询对索引扫描的选择性不够,但仍然没有那么无选择性以至于值得进行全表扫描) 有趣的问题是数据库如何知道查询将返回多少行而不先执行它?它是否在某处存储统计信息,例如不同值的数量与表大小? @LaurentGrégoire:是的,数据库存储有关行数和值分布的统计信息。详见手册:postgresql.org/docs/current/static/planner-stats.html【参考方案2】:

你ANALYZE 表/数据库了吗?那么statistics 呢?当年份 > 2009 的记录很多时,顺序扫描可能比索引扫描快。

【讨论】:

【参考方案3】:

@a_horse_with_no_name 解释得很好。此外,如果您真的想使用索引扫描,通常应该在 where 子句中使用有界范围。例如 - 年份 > 2019 和年份

很多时候统计信息不会在表上更新,并且由于限制可能无法这样做。在这种情况下,优化器将不知道应该在 > 2019 年取多少行。因此它选择顺序扫描来代替完整的知识。大多数时候,有界分区可以解决问题。

【讨论】:

【参考方案4】:

在索引扫描中,读取头从一行跳转到另一行,这比读取下一个物理块(在顺序扫描中)慢 1000 倍。

因此,如果(要检索的记录数 * 1000)小于总记录数,则索引扫描会执行得更好。

【讨论】:

以上是关于为啥 PostgreSQL 对索引列执行顺序扫描?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的 PostgreSQL 数组索引没有被使用(Rails 4)?

为啥此查询在 PostgreSQL 中不使用仅索引扫描?

PostgreSQL citext 索引与较低的表达式索引性能

PostgreSQL 未对 JSONB 上的 GIN 索引使用索引扫描

为啥我在 postgresql 中的视图不使用索引?

PostgreSQL 11 对索引应该足够的分区表进行并行 seq 扫描