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

时间:2017-04-26 16:33:03

标签: 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
);

它包含300万行。

我正在比较以下查询:

EXPLAIN SELECT id
        FROM log
        WHERE log_time < now() - INTERVAL '3 month'
        LIMIT 100000

产生以下计划:

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))

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

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

产生

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))

我有以下问题:

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

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

      

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

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

1 个答案:

答案 0 :(得分:5)

  

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

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

在内部,postgres规划器对顺序和随机读取进行不同的权衡,随机读取通常要贵得多。设置@ComponentScan(basePackageClasses...seq_page_cost决定了这些设置。如果你愿意,可以other settings you can view and tinker with,但我建议你做一些非常保守的修改。

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

  

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

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

排序会改变一切。在这种情况下,计划员说&#34;我们需要按此字段排序,并且我们有一个已经排序的索引,因此以索引顺序从表中读取行,检查约束,直到我们有100000个他们,并返回那套&#34;。

您会注意到第二个查询的成本估算(例如&#39; 0.43..25694.15&#39;)要高得多 - 计划员认为从索引扫描中进行了如此多的随机读取将成本显着高于仅仅一次读取整个表格而没有排序。

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