PostgreSQL 中的 Index-Only 和 Bitmap Index Scan 有什么区别?

Sha*_*Sha 1 postgresql indexing

在我的查询中,我只想使用精确的 where 条件调用数据。这些 where 条件是在 index.js 中创建的。但是解释显示位索引扫描。我不明白为什么。

我的查询如下所示:

Select 
r.spend,
r.date,
...
from metadata m 
inner join 
report r
on m.org_id = r.org_id and m.country_or_region = r.country_or_region and m.campaign_id = r.campaign_id and m.keyword_id = r.keyword_id  
where r.org_id = 1 and m.keyword_type = 'KEYWORD'
offset 0  limit 20 
Run Code Online (Sandbox Code Playgroud)

索引:

Metadata(org_id, keyword_type, country_or_region, campaign_id, keyword_id);
Report(org_id, country_or_region, campaign_id, keyword_id, date);
Run Code Online (Sandbox Code Playgroud)

解释分析:

"Limit  (cost=811883.21..910327.87 rows=20 width=8) (actual time=18120.268..18235.831 rows=20 loops=1)"
"  ->  Gather  (cost=811883.21..2702020.67 rows=384 width=8) (actual time=18120.267..18235.791 rows=20 loops=1)"
"        Workers Planned: 2"
"        Workers Launched: 2"
"        ->  Parallel Hash Join  (cost=810883.21..2700982.27 rows=160 width=8) (actual time=18103.440..18103.496 rows=14 loops=3)"
"              Hash Cond: (((r.country_or_region)::text = (m.country_or_region)::text) AND (r.campaign_id = m.campaign_id) AND (r.keyword_id = m.keyword_id))"
"              ->  Parallel Bitmap Heap Scan on report r  (cost=260773.11..2051875.83 rows=3939599 width=35) (actual time=552.601..8532.962 rows=3162553 loops=3)"
"                    Recheck Cond: (org_id = 479360)"
"                    Rows Removed by Index Recheck: 21"
"                    Heap Blocks: exact=20484 lossy=84350"
"                    ->  Bitmap Index Scan on idx_kr_org_date_camp  (cost=0.00..258409.35 rows=9455038 width=0) (actual time=539.329..539.329 rows=9487660 loops=1)"
"                          Index Cond: (org_id = 479360)"
"              ->  Parallel Hash  (cost=527278.08..527278.08 rows=938173 width=26) (actual time=7425.062..7425.062 rows=727133 loops=3)"
"                    Buckets: 65536  Batches: 64  Memory Usage: 2656kB"
"                    ->  Parallel Bitmap Heap Scan on metadata m  (cost=88007.61..527278.08 rows=938173 width=26) (actual time=1007.028..7119.233 rows=727133 loops=3)"
"                          Recheck Cond: ((org_id = 479360) AND ((keyword_type)::text = 'KEYWORD'::text))"
"                          Rows Removed by Index Recheck: 3"
"                          Heap Blocks: exact=14585 lossy=11054"
"                          ->  Bitmap Index Scan on idx_primary  (cost=0.00..87444.71 rows=2251615 width=0) (actual time=1014.631..1014.631 rows=2181399 loops=1)"
"                                Index Cond: ((org_id = 479360) AND ((keyword_type)::text = 'KEYWORD'::text))"
"Planning Time: 0.492 ms"
"Execution Time: 18235.879 ms"
Run Code Online (Sandbox Code Playgroud)

在这里,我只想调用 20 个项目。应该更有效吧?

ric*_*yen 6

当结果集相对于搜索条件具有高选择性(即,满足搜索条件的行的百分比很高)时,就会发生位图索引扫描。在这种情况下,规划器将计划扫描整个索引,形成一个位图,表示从磁盘上的哪些页面中提取数据(这发生在位图堆扫描步骤期间)。这比顺序扫描要好,因为它只扫描磁盘上的相关页面,跳过它知道相关数据不存在的页面。根据优化器可用的统计信息,执行索引扫描或仅索引扫描可能没有优势,但它仍然比顺序扫描更好。

为了完成问题的答案,仅索引扫描是对索引的扫描,它将拉取相关数据而无需访问实际表。这是因为相关数据已经在索引中。以这张表为例:

postgres=# create table foo (id int primary key, name text);
CREATE TABLE
postgres=# insert into foo values (generate_series(1,1000000),'foo');
INSERT 0 1000000
Run Code Online (Sandbox Code Playgroud)

id该表的列上有一个索引,假设我们调用以下查询:

postgres=# EXPLAIN ANALYZE SELECT * FROM foo WHERE id < 100;
                                                    QUERY PLAN                                                    
------------------------------------------------------------------------------------------------------------------
 Index Scan using foo_pkey on foo  (cost=0.42..10.25 rows=104 width=8) (actual time=0.012..1.027 rows=99 loops=1)
   Index Cond: (id < 100)
 Planning Time: 0.190 ms
 Execution Time: 2.067 ms
(4 rows)
Run Code Online (Sandbox Code Playgroud)

此查询导致索引扫描,因为它扫描 id < 100 的行的索引,然后访问磁盘上的实际表以提取查询*部分中包含的其他列SELECT

但是,假设我们调用以下查询(注意SELECT id而不是SELECT *):

postgres=# EXPLAIN ANALYZE SELECT id FROM foo WHERE id < 100;
                                                      QUERY PLAN                                                       
-----------------------------------------------------------------------------------------------------------------------
 Index Only Scan using foo_pkey on foo  (cost=0.42..10.25 rows=104 width=4) (actual time=0.019..0.996 rows=99 loops=1)
   Index Cond: (id < 100)
   Heap Fetches: 99
 Planning Time: 0.098 ms
 Execution Time: 1.980 ms
(5 rows)
Run Code Online (Sandbox Code Playgroud)

这导致仅索引扫描,因为仅id请求列,并且(自然地)包含在索引中,因此无需访问磁盘上的实际表来检索其他任何内容。这节省了时间,但它的发生非常有限。

要回答您关于限制为 20 个结果的问题,限制发生在位图索引扫描发生之后,因此无论您限制为 20、40 还是其他值,运行时间仍然相同。在索引/仅索引扫描的情况下,执行器将在获取了LIMIT子句指定的足够行后停止扫描。在您的情况下,使用位图堆扫描,这是不可能的