postgresql - PostgreSQL 中的 Index-Only 和 Bitmap Index Scan 有什么区别?
问题描述
在我的查询中,我只想调用具有确切位置条件的数据。这些条件是在索引中创建的。但是解释显示位索引扫描。我不明白为什么。
我的查询如下所示:
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
索引:
Metadata(org_id, keyword_type, country_or_region, campaign_id, keyword_id);
Report(org_id, country_or_region, campaign_id, keyword_id, date);
解释分析:
"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"
在这里,我只想调用 20 个项目。应该更有效?
解决方案
位图索引扫描发生在结果集相对于搜索条件具有高选择性(即,满足搜索条件的行的百分比很高)时。在这种情况下,计划器将计划扫描整个索引,形成一个位图,其中包含磁盘上哪些页面可以从中提取数据(这发生在位图堆扫描步骤中)。这比顺序扫描要好,因为它只扫描磁盘上的相关页面,跳过它知道相关数据不存在的页面。根据优化器可用的统计信息,执行索引扫描或仅索引扫描可能没有优势,但它仍然比顺序扫描更好。
为了完成问题的答案,仅索引扫描是对索引的扫描,它将提取相关数据而无需访问实际表。这是因为相关数据已经在索引中。以这张表为例:
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
该表的列上有一个索引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)
此查询会导致索引扫描,因为它会扫描索引以查找 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)
这会导致仅索引扫描,因为仅id
请求列,并且(自然)包含在索引中,因此无需访问磁盘上的实际表来检索其他任何内容。这节省了时间,但它的发生非常有限。
要回答您关于限制为 20 个结果的问题,限制发生在位图索引扫描发生之后,因此无论您限制为 20、40 还是其他值,运行时间仍然相同。在 Index/Index-Only Scan 的情况下,executor 将在获取足够多的行后停止扫描LIMIT
。在您的情况下,使用位图堆扫描,这是不可能的
推荐阅读
- python - 试图返回一个循环但不返回主父循环
- react-native - 如何使用firebase按类别检索数据
- c# - 无法通过单元测试(httpRequest mock)
- javascript - Highcharts:如何使线接触左侧+右侧
- r - Geom_signif 与 3 组比较
- typescript - 如何将所有这些 `*.d.ts` 文件放在一个模块下?(对于缺少类型的包)
- adal - [ADAL]如何在 ADAL 5.2.4.0 版本中设置 TraceLevel.Warning
- angular - 使用多个 index.html 文件作为输入/输出的 Angular 构建
- c - 在 C 中分配内存 int *p = malloc(n * sizeof(int)); vs int *p = (int*)malloc(3 * sizeof(int));
- awk - 仅连接匹配字符串的行