选择主键:为什么postgres更喜欢顺序扫描和索引扫描

Vla*_*sny 0 postgresql query-optimization

我有下表

create table log
(
    id bigint default nextval('log_id_seq'::regclass) not null
        constraint log_pkey
            primary key,
    level integer,
    category varchar(255),
    log_time timestamp,
    prefix text,
    message text
);
Run Code Online (Sandbox Code Playgroud)

它包含300万行.

我正在比较以下查询:

EXPLAIN SELECT id
        FROM log
        WHERE log_time < now() - INTERVAL '3 month'
        LIMIT 100000
Run Code Online (Sandbox Code Playgroud)

产生以下计划:

Limit  (cost=0.00..19498.87 rows=100000 width=8)
  ->  Seq Scan on log  (cost=0.00..422740.48 rows=2168025 width=8)
        Filter: (log_time < (now() - '3 mons'::interval))
Run Code Online (Sandbox Code Playgroud)

并添加了与ORDER BY id指令相同的查询:

EXPLAIN SELECT id
        FROM log
        WHERE log_time < now() - INTERVAL '3 month'
        ORDER BY id ASC
        LIMIT 100000
Run Code Online (Sandbox Code Playgroud)

产量

Limit  (cost=0.43..25694.15 rows=100000 width=8)
  ->  Index Scan using log_pkey on log  (cost=0.43..557048.28 rows=2168031 width=8)
        Filter: (log_time < (now() - '3 mons'::interval))
Run Code Online (Sandbox Code Playgroud)

我有以下问题:

  • 缺少ORDER BY指令允许Postgres不关心行的顺序.它们也可以按照分类进行交付.为什么没有ORDER BY就不使用索引?

    • Postgres如何在这样的查询中首先使用索引?WHERE查询的子句包含非索引列,并且要获取该列,将需要顺序数据库扫描,但查询ORDER BY不指示.
  • Postgres手册页说:

    对于需要扫描表的大部分的查询,显式排序可能比使用索引更快,因为它遵循顺序访问模式需要更少的磁盘I/O

你能否为我澄清一下这个陈述?索引始终是有序的.读取有序结构总是更快,它总是顺序访问(至少在页面扫描方面)而不是读取非有序数据然后手动排序.

jme*_*sky 5

你能否为我澄清一下这个陈述?索引始终是有序的.读取有序结构总是更快,它总是顺序访问(至少在页面扫描方面)而不是读取非有序数据然后手动排序.

索引按顺序读取,是的,但是postgres需要跟进读取表中的行.也就是说,在大多数情况下,如果索引标识100行,那么postgres将需要对该表执行多达100次随机读取.

在内部,postgres规划器对顺序和随机读取进行不同的权衡,随机读取通常要贵得多.设置seq_page_costrandom_page_cost确定那些.有您可以查看和鼓捣其他设置,如果你想,但我建议是非常保守的修改.

让我们回到你之前的问题:

缺少ORDER BY指令允许Postgres不关心行的顺序.它们也可以按照分类进行交付.为什么没有ORDER BY就不使用索引?

原因是排序.正如您稍后所述,索引不包含约束列,因此使用索引没有任何意义.相反,规划器基本上是说"读取整个表,找出哪些行符合约束,然后以我们找到它们的任何顺序返回它们中的前100000".

排序会改变一切.在这种情况下,规划器说"我们需要按此字段排序,并且我们有一个已经排序的索引,因此按索引顺序从表中读取行,检查约束,直到我们有100000个,并且返回那组".

您会注意到第二个查询的成本估算(例如'0.43..25694.15')要高得多 - 计划员认为从索引扫描中进行如此多的随机读取将花费远远超过仅读取整个查询的成本表一次没有排序.

希望有所帮助,如果您有其他问题,请告诉我.