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

Erw*_*ter 26 postgresql index execution-plan postgresql-9.4

这是对上一个问题的评论的衍生:

使用 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)
Run Code Online (Sandbox Code Playgroud)

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

EXPLAIN ANALYZE SELECT * FROM aa WHERE a BETWEEN 100000 AND 200000;
Run Code Online (Sandbox Code Playgroud)
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))
Run Code Online (Sandbox Code Playgroud)

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

Erw*_*ter 20

正如@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:始终用于位图索引扫描。basic 的输出EXPLAIN不会告诉我们更多。我们从EXPLAIN ANALYZE问题的第二个引用中可以看到更多信息:

Heap Blocks: exact=693 lossy=3732
Run Code Online (Sandbox Code Playgroud)

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

对于有损共享中的页面,必须重新检查索引条件,因为位图只记住要获取哪些页面,而不是页面上的确切元组。并非页面上的所有元组都一定会通过索引条件,实际重新检查条件是必要的。

这是讨论新添加的 pgsql 黑客线程。作者Etsuro Fujita 提供了如何计算最小值work_mem以避免有损位图条目和随后的条件重新检查的公式。对于具有多个位图扫描的复杂情况,该计算不可靠,因此它不用于从EXPLAIN. 它仍然可以作为简单情况的估计。

附加线路 BUFFERS:

此外,当使用BUFFERS选项运行时:EXPLAIN (ANALYZE, BUFFERS) ...添加另一行,如:

Buffers: shared hit=279 read=79
Run Code Online (Sandbox Code Playgroud)

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

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

具体包括共享块命中、读、脏、写数,本地块命中、读、脏、写数,临时块读、写数。

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


Chr*_*ris 11

Erwin,因为这是我们之前在评论线程中的讨论,所以我决定进一步研究一下......

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

SET work_mem = 64;
Run Code Online (Sandbox Code Playgroud)

设置一个非常小的work_mem

SET work_mem = default;
Run Code Online (Sandbox Code Playgroud)

将我的work_mem背部设置为足够大以进行查询。

解释和重新检查条件

因此,仅使用EXPLAINas运行我的查询

EXPLAIN 
SELECT * FROM olap.reading_facts
WHERE meter < 20;
Run Code Online (Sandbox Code Playgroud)

我获得了低和高的结果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)
Run Code Online (Sandbox Code Playgroud)

高的 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)
Run Code Online (Sandbox Code Playgroud)

长话短说,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)
Run Code Online (Sandbox Code Playgroud)

高的 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)
Run Code Online (Sandbox Code Playgroud)

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

结论?(或缺乏)

不幸的是,它看起来像EXPLAIN对自己是不足够的知道是否因为某些行ID的都赞成保留页面的位图堆中扫描过程中被丢弃的索引复核实际上是必要的。

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