PostgreSQL bitmap scan的IO放大的原理解释和优化

本文涉及的产品
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS SQL Server,基础系列 2核4GB
简介:

标签

PostgreSQL , bitmap scan , io 放大 , cpu 放大 , recheck , 数据聚集


背景

PostgreSQL 支持9种索引接口:

《PostgreSQL 9种索引的原理和应用场景》

每一种索引的结构,适合的数据类型,适合的查询场景都不一样。

对于多值类型(例如 K-V,数组、全文检索 类型),我们可以选择GIN倒排索引接口,GIN使用的扫描方法是bitmap scan的扫描方法。

实际上PostgreSQL常用的数据扫描方法包括:

  • seq scan,全表扫描

  • index scan,索引扫描(需要回表)

  • index only scan,索引扫描(通过VM减少回表,大多数情况下,不需要回表)

  • bitmap scan,先扫索引,然后按HEAP BLOCK ID扫描HEAP BLOCK。输出整个数据块的数据,因此需要recheck。

bitmap scan的特性,决定了它可能存在放大(因为一个BLOCK里面哪怕只有一条记录是复合条件的,也会返回整个BLOCK)。

bitmap scan IO,CPU放大例子

1、新建测试表

create table test(id int, arr int[]);  

2、写入测试数据

create or replace function gen_arr(int,int) returns int[] as $$  
  select array(select ($1*random())::int from generate_series(1,$2));  
$$ language sql strict;  
  
postgres=# select gen_arr(100,10);  
            gen_arr              
-------------------------------  
 {5,71,91,23,95,81,98,12,33,2}  
(1 row)  
  
insert into test select id, gen_arr(1000, 10) from generate_series(1,1000000) t(id);  

3、创建索引

create index idx_test_1 on test using gin (arr);  

4、查询,分析

postgres=# explain (analyze,verbose,timing,costs,buffers) select * from test where arr && array[1,2,3];  
                                                          QUERY PLAN                                                             
-------------------------------------------------------------------------------------------------------------------------------  
 Bitmap Heap Scan on public.test  (cost=808.96..13148.92 rows=23402 width=36) (actual time=14.295..52.321 rows=29605 loops=1)  
   Output: id, arr  
   Recheck Cond: (test.arr && '{1,2,3}'::integer[])  
   Heap Blocks: exact=11240  
   Buffers: shared hit=11764  
   ->  Bitmap Index Scan on idx_test_1  (cost=0.00..803.11 rows=23402 width=0) (actual time=12.816..12.816 rows=29605 loops=1)  
         Index Cond: (test.arr && '{1,2,3}'::integer[])  
         Buffers: shared hit=524  
 Planning time: 0.314 ms  
 Execution time: 54.896 ms  
(10 rows)  

包含1或2或3的数据,总共2.9万条,搜索了11240个HEAP BLOCK。

那么我们看看一个BLOCK可以存储多少数据?

postgres=# analyze test;  
ANALYZE  
postgres=# select reltuples/relpages from pg_class where relname='test';  
     ?column?       
------------------  
 80.9978940547546  
(1 row)  

可以存下81条,意味着实际上29605条记录应该只需要365个数据块就可以放下。

但是由于这些目标记录没有密集存储,导致了IO的放大。

那么如何解决这个问题呢?

bitmap scan IO,cpu放大问题优化

1、聚集存储

实现方法很多,这里有一些例子:

《PostgreSQL 黑科技 - 空间聚集存储, 内窥GIN, GiST, SP-GiST索引》

《PostgreSQL GIN 单列聚集索引 应用》

《PostgreSQL 聚集存储 与 BRIN索引 - 高并发行为、轨迹类大吞吐数据查询场景解说》

《K-Means 数据聚集算法》

我们可以看看聚集带来的效果:

-- 重组数据  
postgres=# with tmp as (delete from test where ctid = any(array(select ctid from test where arr && array[1,2,3])) returning *) insert into test select * from tmp;  
INSERT 0 29605  
  
-- 再次查询  
postgres=# vacuum test;  
VACUUM  
postgres=# explain (analyze,verbose,timing,costs,buffers) select * from test where arr && array[1,2,3];  
                                                         QUERY PLAN                                                            
-----------------------------------------------------------------------------------------------------------------------------  
 Bitmap Heap Scan on public.test  (cost=247.16..13321.03 rows=28950 width=65) (actual time=3.459..8.248 rows=29605 loops=1)  
   Output: id, arr  
   Recheck Cond: (test.arr && '{1,2,3}'::integer[])  
   Heap Blocks: exact=367  
   Buffers: shared hit=389  
   ->  Bitmap Index Scan on idx_test_1  (cost=0.00..239.92 rows=28950 width=0) (actual time=3.411..3.411 rows=29605 loops=1)  
         Index Cond: (test.arr && '{1,2,3}'::integer[])  
         Buffers: shared hit=22  
 Planning time: 0.145 ms  
 Execution time: 10.991 ms  
(10 rows)  

现在只访问了367个HEAP数据块。完全避免了IO放大的问题。

实际情况,可以根据业务喜好来聚集。

参考

《PostgreSQL 黑科技 - 空间聚集存储, 内窥GIN, GiST, SP-GiST索引》

《PostgreSQL GIN 单列聚集索引 应用》

《PostgreSQL 聚集存储 与 BRIN索引 - 高并发行为、轨迹类大吞吐数据查询场景解说》

《K-Means 数据聚集算法》

《PostgreSQL 9种索引的原理和应用场景》

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
目录
相关文章
|
7月前
|
缓存 关系型数据库 MySQL
MySQL Binlog--事务日志和BINLOG落盘参数对磁盘IO的影响
MySQL Binlog--事务日志和BINLOG落盘参数对磁盘IO的影响
136 0
|
2月前
|
人工智能 Cloud Native Java
云原生技术深度解析:从IO优化到AI处理
【10月更文挑战第24天】在当今数字化时代,云计算已经成为企业IT架构的核心。云原生作为云计算的最新演进形态,旨在通过一系列先进的技术和实践,帮助企业构建高效、弹性、可观测的应用系统。本文将从IO优化、key问题解决、多线程意义以及AI处理等多个维度,深入探讨云原生技术的内涵与外延,并结合Java和AI技术给出相应的示例。
109 1
|
7月前
|
存储 监控 关系型数据库
MySQL 参数innodb_read_io_threads
`innodb_read_io_threads` 是 MySQL 数据库中 InnoDB 存储引擎的一个配置参数,它用于指定后台线程池中用于处理读取 I/O 请求的线程数量。InnoDB 存储引擎负责管理数据库的物理存储和检索,是 MySQL 最常用的存储引擎之一。 ### 参数说明 - **名称**: `innodb_read_io_threads` - **默认值**: 4 - **范围**: 1 到 64 - **动态修改**: 不能动态修改(需要重启服务器) - **适用版本**: MySQL 5.6 及以上版本 ### 作用 `innodb_read_io_threads`
645 1
|
4月前
|
缓存 Linux 调度
[kvm]硬盘IO优化
[kvm]硬盘IO优化
|
7月前
|
设计模式 缓存 Java
【Java技术专题】「入门到精通系列教程」深入探索Java特性中并发编程体系的原理和实战开发指南( 实现可伸缩IO专题)— 上
【Java技术专题】「入门到精通系列教程」深入探索Java特性中并发编程体系的原理和实战开发指南( 实现可伸缩IO专题)— 上
80 0
|
6月前
|
存储 缓存 NoSQL
Redis为什么速度快:数据结构、存储及IO网络原理总结
Redis为什么速度快:数据结构、存储及IO网络原理总结
|
6月前
|
Java 开发者
Java IO流实战技巧:如何优化InputStream/OutputStream和Reader/Writer的使用?
【6月更文挑战第26天】Java IO流优化涉及缓冲、资源管理、字符编码和流式处理。使用Buffered流提高读写效率,如`BufferedInputStream`和`BufferedReader`。确保资源关闭使用try-with-resources,如`try (InputStream is = ...) {...}`。处理文本时指定编码,如`InputStreamReader(is, StandardCharsets.UTF_8)`防止乱码。流式处理大文件,分块读写避免内存溢出,以减少内存占用。这些技巧能提升程序性能和健壮性。
253 0
|
6月前
|
SQL 数据处理 API
实时计算 Flink版产品使用问题之holo的io以及cpu使用较为稳定,sink端busy一直在20%左右,有时候50%,该如何优化
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
7月前
|
缓存 Java 数据库
深入理解 Java IO 流:原理与应用
【4月更文挑战第4天】Java IO 浴是处理文件和数据输入输出的关键,涉及数据传输和处理。核心概念包括输入流(从源头读取)和输出流(写入目标),由InputStream和OutputStream接口定义基础操作。具体实现如FileInputStream和FileOutputStream用于文件操作,BufferedInputStream和BufferedOutputStream提升性能。IO流广泛应用于文件操作、网络通信、数据库交互和系统交互。其优点在于灵活性、可扩展性和高效性。使用时注意关闭流、处理异常及选择合适流实现。理解IO流原理和应用能提升编程效率和程序性能。
180 1
|
7月前
|
传感器 编解码 C语言
【软件设计师备考 专题 】IO设备、通信设备的性能,以及基本工作原理
【软件设计师备考 专题 】IO设备、通信设备的性能,以及基本工作原理
93 1

相关产品

  • 云原生数据库 PolarDB
  • 云数据库 RDS PostgreSQL 版