非常简单的例子-一个表,一个索引,一个查询:

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)

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


当前回答

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

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

顺便说一句:这对其他DBMS也是正确的-一些优化,如“只扫描索引”被搁置一边(但对于SELECT *,这样的DBMS不太可能会去“只扫描索引”)

其他回答

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

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

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

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

顺便说一句:这对其他DBMS也是正确的-一些优化,如“只扫描索引”被搁置一边(但对于SELECT *,这样的DBMS不太可能会去“只扫描索引”)

你分析表/数据库了吗?那么统计数据呢?当有许多年为> 2009的记录时,顺序扫描可能比索引扫描快。

@ a_horis_with_no_name解释得很好。另外,如果你真的想使用索引扫描,你通常应该在where子句中使用有界的范围。例如, >年2019年和< 2020年。

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