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


150

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

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)

为什么不执行索引扫描呢?我想念什么?

Answers:


222

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

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

顺便说一句:其他DBMS也是这样-取消了一些优化,例如“仅索引扫描”(但是对于SELECT *,这种DBMS不太可能会进行“仅索引扫描”)


12
5-10%取决于几个配置设置以及数据的存储。这不是一个硬数字。
Frank Heikens 2011年

6
@弗兰克:这就是为什么我说“大约” :)但感谢您指出这一点
a_horse_with_no_name 2011年

5
同样,顺序扫描可以一次从堆中请求几页,并要求内核在当前块上运行时获取下一个块-索引扫描一次获取一个页面。(位图扫描会在两者之间做出折衷,通常会在查询计划中出现,这些查询的选择性不足以进行索引扫描,但仍然没有选择不足以值得进行全表扫描)
araqnid

4
有趣的问题是,如果不先执行查询,数据库如何知道查询将返回多少行?它是否在某些地方存储统计信息,例如不同值的数量与表大小的关系?
LaurentGrégoire'10

7
@LaurentGrégoire:是的,数据库存储有关行数和值分布的统计信息。有关详细信息,请参见手册:postgresql.org/docs/current/static/planner-stats.html
a_horse_with_no_name


0

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

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


0

@a_horse_with_no_name解释得很好。另外,如果您确实要使用索引扫描,则通常应在where子句中使用有界范围。例如-> 2019年和<2020年。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.