PostgreSQL执行计划:Bitmap scan VS index only scan
之前了解过postgresql的Bitmap scan,只是粗略地了解到是通过标记数据页面来实现数据检索的,执行计划中的的Bitmap scan一些细节并不十分清楚。这里借助一个执行计划来分析bitmap scan以及index only scan,以及两者的一些区别。
这里有关于Bitmap scan的一些实现过程,https://dba.stackexchange.com/questions/119386/understanding-bitmap-heap-scan-and-bitmap-index-scan0. 构建测试环境
如下测试脚本,构建一个简单的测试表
create table my_test_table01
(
c1 serial not null primary key,
c2 varchar(100),
c3 timestamp
)
--c3字段上建索引
create index ix_c3 on my_test_table01(c3); truncate table my_test_table01; --写入300W行测试数据,c3列生成随机时间
insert into my_test_table01 (c2,c3)
select uuid_generate_v1(),NOW() - (random() * (NOW()+'1000 days' - NOW())) from generate_series(1,3000000);
1. Bitmap Scan的剖析
用最最容易理解的场景来测试Bitmap Index Scan,执行如下sql,来分析bitma scan这个执行计划的含义。
explain (analyze, buffers,verbose,timing)
select count(1) from my_test_table01 a
where a.c3 >'20220328' or a.c1 < 100;

对以上的执行计划,有几个问题先弄清楚:
1,Bitmap Index Scan做了什么?
2,Bitmap Heap Scan做了什么?
3,Recheck Cond的目的是什么?
第一个问题:Bitmap Index Scan做了什么?
一个查询条件的Bitmap是一个bit数组,bit数组中的每一位映射到表中的一个数据页Id(One bit per heap page, in the same order as the heap)。
Bitmap Index Scan对于所有的查询条件,从扫描索引的所有页面,如果数据页面中有复合条件的数据,那么就将bit为标记为1,否则标记为0。
其他的查询条件依次创建一个一样的bit数组,同样扫描索引的所有页面,将符合条件的page的bit位标记为1。
最后多个条件生成的多个bit数组进行与(或)操作(取决于where多个条件是and组合或者or组合,上面截图中的BitmapOr),合并成一个最终的bit数组。
此时最终的bit数组标记的复合条件的数据页,而不是最终的数据行,所以最终还要去数据页中进行筛选。
bitmap index scan 内部优化机制:https://www.postgresql.org/message-id/12553.1135634231@sss.pgh.pa.us
第二个问题:Bitmap Heap Scan做了什么
而BitMap Index Scan一次性将满足条件的索引项全部取出,并在内存中进行排序, 然后根据取出的索引项访问表数据,也就是执行计划中的Bitmap Heap Scan。
第三个问题:Recheck Cond的目的是什么
BitMap Heap Scan指示找到符合条件的数据页面,而不是具体的记录,此时找到数据页后再用where条件进行筛选,也就是执行计划中的Recheck Cond。
https://stackoverflow.com/questions/50959814/what-does-recheck-cond-in-explain-result-mean
If the bitmap gets too large we convert it to "lossy" style, in which we only remember which pages contain matching tuples instead of remembering each tuple individually. When that happens, the table-visiting phase has to examine each tuple on the page and recheck the scan condition to see which tuples to return.
bitmap scan示例图
图片来源:https://www.cybertec-postgresql.com/en/postgresql-indexing-index-scan-vs-bitmap-scan-vs-sequential-scan-basics/bitmap index scan不仅仅发生在where条件中有多个筛选条件的场景(比如where c1 = m and c2 =n),其实对于一个条件的范围查询,也同样适用bitmap index scan,见下例。
2. 为什么执行计划走Bitmap Index Scan,而不是Index only Scan?
对于如下这个查询,表中有300W测试数据符合条件的数据比例很少,很明显,ix_c3上的索引扫描才是更优化的执行计划,为什么在默认情况下是Bitmap Index Scan?
select count(1) from my_test_table01 a
where a.c3 >'20220328' ;
从如下截图可以看到,vacuum是打开的,在造完测试数据后,默认情况下上述sql查询走了bitmap Index scan,因为c3上有索引,预期是走ix_c3上的索引。
原本以为vacuum是异步的,或者说有滞后性,但是这个case在测试数据构造完之后几个小时甚至几天,该查询都依旧走bitmap Index scan的方式。
当关闭enable_bitmapscan和enable_seqscan,强制优化器走ix_c3上的index only scan,代价明显更大,这就有点说不通了,原因下文会具体分析。
本人对该现象一开始也是百思不得其解,难道是bitmap scan有什么魔法?
看到这里有一个提到这个问题:https://www.datadoghq.com/blog/postgresql-vacuum-monitoring/,里面相关的内容的是这么说的:
1. Large insert-only tables. Large insert-only tables are not automatically vacuumed (except for transaction-ID wraparound), because autovacuum is triggered by updates and deletes. This is generally a good thing, because it saves a great deal of not-very-useful work. However, it's problematic for index-only scans, because it also means the visibility map bits won't get set. I don't have a very clear idea what to do about this, but it's likely to need thought and work. For a first version of this feature, we'll likely have to rely on users to do a manual VACUUM in this case.
既然这种场景无法主从出发vacuum,那么这里就手动vacuum测试表,然后打开bitmap scan选项,继续观察此时的默认情况下,该查询是不是可以走index only scan,这一次终于是预期的ix_c3上的index only scan了。
同时还有一个疑问:对表执行vacuum前后,index only scan的shared hit差别这么大?
上述得知在large-insert的情况下,不会触发表上的vacuum,此时如果强制使用index only scan,因为索引上的没有数据行的可见性信息(Index Only Scan operation must visit the heap to check if the row is visible.)所以在vacuum之前,强制使用index only scan的过程中,对于任何一行数据都要回表进行可见性判断,因此会产生大量的shared hit。一旦vacuum之后,由于索引上更新了数据行的可见性,不需要回表判断,因此shared hit会大幅度地降低。
3. 主动触发vacuum.
Large insert-only tables are not automatically vacuumed,也就是大批量的插入无法主动发出vacuum,vacuum由update和delete产生,那么尝试对表执行一些update或者delete操作,会不会主动触发vacuum?
基于第一步的脚本,重新初始化测试表,在插入300W行数据后,删除其中一部分数据,目前是让delete操作触发vacuum,然后再通过执行计划,观察是否会想手动vacuum一样,走index only scan。
经过三次删除,完美触发vacuum,执行计划有一开始bitmap scan更新为index only scan。
4. bitmp index scan VS index-only scan
参考这里https://www.cybertec-postgresql.com/en/killed-index-tuples/ 对 bitmap index scan 和 index-only scan的解释
PostgreSQL 8.1 introduced the “bitmp index scan”. This scan method first creates a list of heap blocks to visit and then scans them sequentially.
This not only reduces the random I/O, but also avoids that the same block is visited several times during an index scan. PostgreSQL 9.2 introduced the “index-only scan”, which avoids fetching the heap tuple.
This requires that all the required columns are in the index and the “visibility map” shows that all tuples in the table block are visible to everybody.
bitmp index scan不仅可以避免随机的IO操作,而且可以避免同一个页面(在一个查询执行过程中)被重复读取(一个页面中可能存在多条满足查询条件的元组,其他方式可能会多次读取同一个页面)。
index-only scan避免了从堆中读取数据,但是他要求所有请求的字段都在索引中,并且“visibility map” 中显示表块中的所有元组对所有事物都是可见的,但是索引中并不包含元组的可见性。
本文通过一个看似不起眼的问题sql执行计划的分析,尝试分析bitmap scan 和index only scan的差异以及选择二者的原因,同时会涉index索引元组的可见性及vacuum没有触发的一些特殊场景。一个问题往往不是一个点,是一系列问题的合集,此事要躬行。
参考链接:
PostgreSQL执行计划:Bitmap scan VS index only scan的更多相关文章
- 深入理解Oracle索引(1):INDEX SKIP SCAN 和 INDEX RANGE SCAN
㈠ Index SKIP SCAN 当表有一个复合索引,而在查询中有除了索引中第一列的其他列作为条件,并且优化器模式为CBO,这时候查询计划就有可能使用到SS ...
- PostgreSQL 执行计划
简介 PostgreSQL是“世界上最先进的开源关系型数据库”.因为出现较晚,所以客户人群基数较MySQL少,但是发展势头很猛,最大优势是完全开源. MySQL是“世界上最流行的开源关系型数据库”.当 ...
- PostgreSQL执行计划的解析
一个顺序磁盘页面操作的cost值由系统参数seq_page_cost (floating point)参数指定的,由于这个参数默认为1.0,所以我们可以认为一次顺序磁盘页面操作的cost值为1.下面o ...
- postgreSQL执行计划
" class="wiz-editor-body wiz-readonly" contenteditable="false"> explain命 ...
- INDEX FAST FULL SCAN和INDEX FULL SCAN
INDEX FULL SCAN 索引全扫描.单块读 .它扫描的结果是有序的,因为索引是有序的.它通常发生在 下面几种情况(注意:即使SQL满足以下情况 不一定会走索引全扫描) 1. SQL语句有ord ...
- index unique scan 与index range scan等的区别
存取Oracle当中扫描数据的方法(一) Oracle 是一个面向Internet计算环境的数据库.它是在数据库领域一直处于领先地位的甲骨文公司的产品.可以说Oracle关系数据库系统是目前世界上流行 ...
- oralce索引中INDEX SKIP SCAN 和 INDEX RANGE SCAN区别
INDEX SKIP SCAN 当表中建立有复合索引的时候,查询时,除复合索引第一列外,别的列作为条件时,且优化器模式为CBO,这个时候查询可能会用到INDEX SKIP SCAN skip scan ...
- [20180316]为什么不使用INDEX FULL SCAN (MIN/MAX).txt
[20180316]为什么不使用INDEX FULL SCAN (MIN/MAX).txt --//链接:http://www.itpub.net/thread-2100456-1-1.html.自己 ...
- PostgreSQL 与 Oracle 访问分区表执行计划差异
熟悉Oracle 的DBA都知道,Oracle 访问分区表时,对于没有提供分区条件的,也就是在无法使用分区剪枝情况下,优化器会根据全局的统计信息制定执行计划,该执行计划针对所有分区适用.在分析利弊之前 ...
随机推荐
- (转载)字符编码那点事:快速理解ASCII、Unicode、GBK和UTF-8
- 基于Zookeeper的分布式锁(干干干货)
原文地址: https://juejin.im/post/5df883d96fb9a0163514d97f 介绍 为什么使用锁 锁的出现是为了解决资源争用问题,在单进程环境下的资源争夺可以使用 JDK ...
- Mysql-基础-DDL-数据定义语言
一.SQL通用语法 SQL 语法可以单行或者多行书写,以分号结尾. SQL 语句可以使用空格/缩进来增强语句的可读性. Mysql 数据库的 SQL 语句不区分大小写,关键字建议使用大写. 注释: 单 ...
- LGP7840题解
给出一种新的理解方式,本质上和正解是一致的. 首先我们现在已经有了一个森林,我们现在要给他加一条边,加哪一条边是最优的呢? 假设加的边是 \((u,v)\),那么 \(((d[u]+1)^2-d[u] ...
- ROS第一次课作业分享
ROS第一次课作业分享 2021年夏季学期学院开设了ROS的相关课程,最近在复习相关知识,正好做一下整理.下面是第一次作业的要求: 编写一个ROS节点,具备以下功能: 读取小海龟仿真器的/turtle ...
- 使用MariaDB backup恢复主从
安装 yum install MariaDB-backup 备份命令 工具需要直接操作数据目录,需要在数据库服务器上执行 mariabackup --backup --target-dir=/data ...
- Centos7下开启防火墙,允许通过的端口
1.查看防火墙状态 systemctl status firewalld 2.如果不是显示active状态,需要打开防火墙 systemctl start firewalld 3.# 查看所有已开放的 ...
- eclipse中的项目如何打成war包
war包即Web归档文件,将Web项目打成war包可以直接拷贝到Web服务器发布目录(例如Tomcat服务器webapps目录 ),当Tomcat启动后该压缩文件自动解压,war包方便了web工程的发 ...
- ZYNQ SGI、PPI、SPI三种中断的实例(含代码)
ZYNQ中断分为3类: SGI(Software Generated Interrupts)软件中断 PPI(Private Peripheral Interrupts)私有外设中断 SPI(Shar ...
- django之分页,纯python代码
Django中分页 py文件代码 """ 自定义分页组件 可以返回分页的数据和分页的HTML代码 """ from django.http ...