各种类型的索引扫描的共享缓冲区指标代表什么?

Mar*_*eth 7 postgresql index explain postgresql-9.3

TL;DR:当“EXPLAIN (BUFFERS)”显示“索引扫描”时,它不会显示需要从索引中读取的页数。它只是省略了那个数字,还是它实际上不读取页面(我误解了索引的工作原理)?

我们有一个大的只读表,看起来像这样:

database=> \d my_table
    Table "my_table"
        Column         |       Type       | Modifiers
-----------------------+------------------+-----------
 id                    | integer          |
 date                  | date             |
 country_id            | smallint         |
 ...other columns...
Indexes:
    "my_table_id_date_idx" btree (id, date)
Run Code Online (Sandbox Code Playgroud)

并且对该表的典型查询具有这样的 EXPLAIN...

database=> EXPLAIN (BUFFERS, ANALYZE) SELECT id, date, country_id FROM my_table WHERE id = 50 AND date BETWEEN '2015-04-01' AND '2015-04-07';
                                                                                    QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on my_table  (cost=448.40..70400.85 rows=18810 width=10) (actual time=9.011..1447.817 rows=10224 loops=1)
   Recheck Cond: ((id = 50) AND (date >= '2015-04-01'::date) AND (date <= '2015-04-07'::date))
   Buffers: shared hit=232 read=9994
   ->  Bitmap Index Scan on my_table_id_date_idx  (cost=0.00..443.69 rows=18810 width=0) (actual time=6.467..6.467 rows=10224 loops=1)
         Index Cond: ((id = 50) AND (date >= '2015-04-01'::date) AND (date <= '2015-04-07'::date))
         Buffers: shared hit=2 read=30
 Total runtime: 1450.175 ms
(7 rows)
Run Code Online (Sandbox Code Playgroud)

这是我如何分析这个:

  1. 该查询将执行“位图索引扫描”,这需要它读取 32 页。到目前为止,我一直假设这 32 页包含索引本身的数据。最后,它会创建一个要阅读的页面列表。
  2. 查询然后执行“位图堆扫描”,这将排序要读取的页面,然后读取它们。最后,这读取了超过 10k 页。

我们最近在其索引上将此(只读)表更改为 CLUSTER,这大大减少了需要读取的页面。现在解释看起来像这样......

database=> EXPLAIN (BUFFERS, ANALYZE) SELECT id, date, country_id FROM my_table WHERE id = 50 AND date BETWEEN '2015-04-01' AND '2015-04-07';
                                                                                              QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Index Scan using my_table_id_date_idx on my_table  (cost=0.57..36703.31 rows=20594 width=10) (actual time=0.029..4.830 rows=10224 loops=1)
   Index Cond: ((id = 50) AND (date >= '2015-04-01'::date) AND (date <= '2015-04-07'::date))
   Buffers: shared hit=160
 Total runtime: 6.658 ms
(4 rows)
Run Code Online (Sandbox Code Playgroud)

在这种情况下,它正在执行索引扫描。据我所知,索引扫描和位图索引/堆扫描之间的区别在于,前者将按照索引定义的顺序读取页面,而后者将创建要读取的页面位图(可能来自多个索引) ),对结果进行排序,并按顺序阅读。在我看来,在这两种情况下,都需要读取索引页才能实际确定要读取的结果数据页。

但是,在“索引扫描”中,“缓冲区”行有 160 页,根据我的其他测试,这是实际数据的页数,不包括我在上面的位图索引扫描中看到的 32 页索引本身。

database=> set enable_indexscan to false;
SET
database=> EXPLAIN (BUFFERS, ANALYZE) SELECT id, date, country_id FROM my_table WHERE id = 50 AND date BETWEEN '2015-04-01' AND '2015-04-07';
                                                                                    QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on my_table  (cost=491.14..76781.03 rows=20594 width=10) (actual time=1.003..3.873 rows=10224 loops=1)
   Recheck Cond: ((id = 50) AND (date >= '2015-04-01'::date) AND (date <= '2015-04-07'::date))
   Buffers: shared hit=160
   ->  Bitmap Index Scan on my_table_id_date_idx  (cost=0.00..486.00 rows=20594 width=0) (actual time=0.981..0.981 rows=10224 loops=1)
         Index Cond: ((id = 50) AND (date >= '2015-04-01'::date) AND (date <= '2015-04-07'::date))
         Buffers: shared hit=32
 Total runtime: 5.595 ms
(7 rows)
Run Code Online (Sandbox Code Playgroud)

这是否意味着索引扫描实际上并未从索引中读取?那么它如何知道要阅读哪些页面呢?索引扫描解释输出中是否省略了刚刚读取的索引页数?或者我对位图索引扫描中的那个值代表什么的理解是不正确的。

Cra*_*ger 5

在这种情况下,它正在执行索引扫描。据我所知,索引扫描和位图索引/堆扫描之间的区别在于,前者将按照索引定义的顺序读取页面,而后者将创建要读取的页面位图(可能来自多个索引) ),对结果进行排序,并按 [heap] 顺序读取它们。

正确的。

还有仅索引扫描,其中读取索引以直接满足查询,并且对于读取的大多数索引页没有堆提取。如果系统不能确保页面对所有事务都可见,则仍然需要堆提取,因此它就像带有快捷方式的索引扫描。

在我看来,在这两种情况下,都需要读取索引页才能实际确定要读取的结果数据页。

正确的。

但是,在“索引扫描”中,“缓冲区”行有 160 页,根据我的其他测试,这是实际数据的页数,不包括我在上面的位图索引扫描中看到的 32 页索引本身。

我认为这是误解。该行将是从索引和堆读取的页面总和。不是所有的索引页,也不是所有的堆页,都需要被读取。

另一个重要的注意事项是“缓冲区”指标,就像 EXPLAIN 中的其他指标一样,是累积的​​:在父节点中读取的页数包括子节点的页数。因此,命中的 160 个缓冲区包括索引读取。实际上,在最后两种情况下,仅从堆中读取了 128 页。