使用位图索引扫描的查询计划中的“重新检查条件:”行


21

这是从注释到上一个问题的副产品:

使用PostgreSQL 9.4,Recheck Cond:在所输出的查询计划中的位图索引扫描之后,似乎总是出现一行EXPLAIN

就像EXPLAIN所引用问题的输出中一样:

->  Bitmap Heap Scan on table_three  (cost=2446.92..19686.74 rows=8159 width=7)
      Recheck Cond: (("timestamp" > (now() - '30 days'::interval)) AND (client_id > 0))
      ->  BitmapAnd  (cost=2446.92..2446.92 rows=8159 width=0)
            ->  Bitmap Index Scan on table_one_timestamp_idx  (cost=0.00..1040.00 rows=79941 width=0)
                  Index Cond: ("timestamp" > (now() - '30 days'::interval))
            ->  Bitmap Index Scan on fki_table_three_client_id  (cost=0.00..1406.05 rows=107978 width=0)
                  Index Cond: (client_id > 0)

或在EXPLAIN ANALYZE一个简单的巨大表的输出中(只有很少的work_mem):

EXPLAIN ANALYZE SELECT * FROM aa WHERE a BETWEEN 100000 AND 200000;
Bitmap Heap Scan on aa  (cost=107.68..4818.05 rows=5000 width=4) (actual time=27.629..213.606 rows=100001 loops=1)
  Recheck Cond: ((a >= 100000) AND (a <= 200000))
  Rows Removed by Index Recheck: 758222
  Heap Blocks: exact=693 lossy=3732
  ->  Bitmap Index Scan on aai  (cost=0.00..106.43 rows=5000 width=0) (actual time=27.265..27.265 rows=100001 loops=1)
        Index Cond: ((a >= 100000) AND (a <= 200000))

这是否意味着在位图索引扫描之后必须再次检查索引条件?
我们还能从EXPLAIN输出中学到什么?

Answers:


17

正如@Chris在提到的问题上正确评论的那样

稍作调查似乎表明,重新检查条件始终打印在中EXPLAIN,但实际上仅在work_mem足够小的位图有损时才执行 。有什么想法吗? http://www.postgresql.org/message-id/464F3C5D.2000700@enterprisedb.com

尽管这都是事实,并且核心开发人员Heikki Linnakangas是头等舱消息来源,但该帖子可以追溯到2007年(Postgres 8.2)。这是Michael Paquier博客文章,其中对Postgres 9.4进行了详细说明,并在其中EXPLAIN ANALYZE提供了更多信息,改善了输出。

对于位图索引扫描,该Recheck Cond:始终存在。基本的输出EXPLAIN不会告诉我们更多信息。我们可以从EXPLAIN ANALYZE问题的第二个引号中获得更多信息:

Heap Blocks: exact=693 lossy=3732

在总共4425个数据页(块)中,有693个正好存储了元组(包括元组指针),而其他3732页在位图中是有损的(仅是数据页)。当work_mem它的大小不足以完全存储从索引扫描构建的整个位图(无损)时,就会发生这种情况。

必须针对有损共享中的页面重新检查索引条件,因为位图仅记住要提取的页面,而不记住页面上的确切元组。不是网页上的所有元组必然通过指数的条件下,有必要真正重新检查条件。

这是pgsql黑客上讨论新增加内容的线程。作者Etsuro Fujita提供了有关如何计算最小值work_mem以避免有损位图条目以及随后进行条件重新检查的公式。对于具有多次位图扫描的复杂情况,该计算不可靠,因此未用于从中输出实际数字EXPLAIN。它仍然可以作为简单案例的估计。

附加线 BUFFERS:

另外,当运行带有BUFFERSoption的选项时EXPLAIN (ANALYZE, BUFFERS) ...

Buffers: shared hit=279 read=79

这表明从缓存(shared hit=279)中读取了多少基础表(和索引),以及必须从磁盘(read=79)中获取了多少。如果您重复查询,对于不太大的查询,“读取”部分通常会消失,因为所有内容都将在第一次调用后立即缓存。第一个电话告诉您已经缓存了多少。后续调用显示您的缓存可以处理多少(当前)。

还有更多选择。关于BUFFERS选项的手册:

具体来说,包括命中,读取,弄脏和写入的共享块的数量,命中,读取,弄脏和写入的本地块的数量以及读写的临时块的数量。

继续阅读,还有更多。
这是源代码中输出选项的列表。


10

欧文(Erwin),由于这是我们之前在评论线程中进行的讨论,因此我决定进一步讨论一下...

我有一个合理大小的表中的非常简单的查询。我通常有足够的work_mem,但是在这种情况下,我使用了命令

SET work_mem = 64;

设置一个非常小的work_mem

SET work_mem = default;

使work_mem我的查询足够大。

解释并重新检查条件

所以,我跑与查询仅EXPLAIN作为

EXPLAIN 
SELECT * FROM olap.reading_facts
WHERE meter < 20;

我获得了低和高的结果work_mem

work_mem

Bitmap Heap Scan on reading_facts  (cost=898.92..85632.60 rows=47804 width=32)
  Recheck Cond: (meter < 20)
  ->  Bitmap Index Scan on idx_meter_reading_facts  (cost=0.00..886.96 rows=47804 width=0)
        Index Cond: (meter < 20)

work_mem

Bitmap Heap Scan on reading_facts  (cost=898.92..85632.60 rows=47804 width=32)
  Recheck Cond: (meter < 20)
  ->  Bitmap Index Scan on idx_meter_reading_facts  (cost=0.00..886.96 rows=47804 width=0)
        Index Cond: (meter < 20)

长话短说,EXPLAIN仅出于预期,查询计划表明可能存在重新检查条件,但我们无法确定是否会实际计算出该条件。

解释分析并重新检查条件

当我们ANALYZE将查询包括在内时,结果将向我们传达更多有关我们需要了解的信息的信息。

work_mem

Bitmap Heap Scan on reading_facts  (cost=898.92..85632.60 rows=47804 width=32) (actual time=3.130..13.946 rows=51840 loops=1)
  Recheck Cond: (meter < 20)
  Rows Removed by Index Recheck: 86727
  Heap Blocks: exact=598 lossy=836
  ->  Bitmap Index Scan on idx_meter_reading_facts  (cost=0.00..886.96 rows=47804 width=0) (actual time=3.066..3.066 rows=51840 loops=1)
        Index Cond: (meter < 20)

work_mem

Bitmap Heap Scan on reading_facts  (cost=898.92..85632.60 rows=47804 width=32) (actual time=2.647..7.247 rows=51840 loops=1)
  Recheck Cond: (meter < 20)
  Heap Blocks: exact=1434
  ->  Bitmap Index Scan on idx_meter_reading_facts  (cost=0.00..886.96 rows=47804 width=0) (actual time=2.496..2.496 rows=51840 loops=1)
        Index Cond: (meter < 20)

再次,正如预期的那样,包含会ANALYZE向我们揭示一些非常重要的信息。在低级work_mem情况下,我们看到索引重新检查删除了一些行,并且我们有lossy堆块。

结论?(或缺少)

不幸的是,看起来仅仅EXPLAIN靠自身不足以知道是否实际上需要重新进行索引,因为某些行ID被丢弃,以便在位图堆扫描期间保留页面。

使用EXPLAIN ANALYZE来诊断中等长度查询的问题是很好的方法,但是如果查询要花很长时间才能完成,然后运行EXPLAIN ANALYZE以发现由于位图索引不足导致位图索引转换为有损work_mem仍然是一个困难的约束。我希望有一种方法可以EXPLAIN从表格统计信息中估计发生这种情况的可能性。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.