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

Ale*_*yda 132 postgresql indexing sequence database-scan

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

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
Run Code Online (Sandbox Code Playgroud)

给我:

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

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

a_h*_*ame 201

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

这是因为索引扫描需要为每行执行多个 IO操作(查找索引中的行,然后从堆中检索行).虽然顺序扫描每行只需要一个IO - 或者甚至更少,因为磁盘上的块(页面)包含多行,因此可以使用单个IO操作获取多个行.

顺便说一句:对于其他DBMS也是如此 - 一些优化作为"仅索引扫描"而被忽略(但对于SELECT*,这样的DBMS极不可能用于"仅索引扫描")

  • 5-10%还取决于几个配置设置和数据存储.这不是一个难题. (12认同)
  • @Frank:这就是为什么我说"大约":)但是感谢你指出它 (6认同)
  • 此外,顺序扫描可以一次从堆中请求多个页面,并要求内核在当前工作的情况下获取下一个块 - 索引扫描一次读取一个页面.(位图扫描在两者之间进行折衷,您通常会看到计划中出现的查询对于索引扫描而言不够有选择性,但仍然不是那么无选择以至于值得进行全表扫描) (4认同)
  • @LaurentGrégoire:是的,数据库存储有关行数和值分布的统计信息.有关详细信息,请参阅手册:https://www.postgresql.org/docs/current/static/planner-stats.html (4认同)
  • 有趣的问题是数据库如何知道查询将返回多少行而不先执行它?它是否存储统计数据,例如不同值的数量与表格大小的位置? (2认同)

Fra*_*ens 12

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


Gau*_*ema 6

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

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


小智 5

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

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