2017-04-26 47 views
0

我有如下表选择主键:为什么Postgres的喜欢做连续扫描VS索引扫描

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

它包含样3万行的。

我比较以下查询:

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由于以下顺序访问模式

能否请您澄清这个说法我吗?索引总是有序的。读取有序结构总是更快,它总是一个顺序访问(至少在页面扫描方面)比读取无序数据,然后手动排序。

回答

4

您能否为我澄清一下这句话?索引总是有序的。读取有序结构总是更快,它总是一个顺序访问(至少在页面扫描方面)比读取无序数据,然后手动排序。

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

在内部,postgres计划者权衡顺序和随机读取不同,随机读取通常更昂贵。设置seq_page_costrandom_page_cost确定那些。如果你愿意,还有other settings you can view and tinker with,不过我建议你在修改时保持非常保守。

让我们回到你刚才的问题:

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

原因是这样的。正如你后面提到的那样,索引不包括约束列,所以使用索引没有任何意义。相反,计划者基本上是说:“读整个表格,找出哪些行符合约束条件,然后按照我们找到的任何顺序返回前10万行”。

排序改变了事情。在这种情况下,计划者说“我们需要按这个字段进行排序,并且我们有一个已经排序的索引,所以按照索引顺序从表中读取行,检查约束,直到我们有100000个,并且返回该集合“。

你会注意到,费用估计(如“0.43..25694.15”)是第二个查询高得多 - 策划者认为,这样做很多是从索引扫描随机读取要花费比显著更多只需一次阅读整个表格,无需分类。

希望有帮助,并让我知道你是否还有其他问题。