PostgreSQL 多查询条件,多个索引的选择算法与问题诊断方法

本文涉及的产品
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
RDS AI 助手,专业版
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
简介:

标签

PostgreSQL , 多列条件 , 多索引 , 单列索引 , 复合索引 , 联合索引 , 优化器 , 评估 , 行评估 , 成本


背景

当一个SQL中涉及多个条件,并且多个条件有多种索引可选时,数据库优化器是如何选择使用哪个索引的?

例如

有一张表,有2个字段,单列一个索引,双列一个复合索引.

建表。  
postgres=# create table tbl(id int, gid int);  
CREATE TABLE  
  
插入1000万记录,其中ID唯一,GID只有10个值。  
postgres=# insert into tbl select generate_series(1,10000000), random()*9 ;  
INSERT 0 10000000  
  
创建两个索引。  
postgres=# create index idx1 on tbl(id);  
CREATE INDEX  
postgres=# create index idx2 on tbl(gid,id);  
CREATE INDEX  

下面三条SQL,会如何选择使用哪个索引呢?

select * from tbl where id in (1,2,3,4,5,6,7,8,9,10) and gid=123;  
  
select * from tbl where id in (1,2,3,4,5,6,7,8,9,10) and gid=1;  
  
select * from tbl where id in (1,2,3,4,5,6,7,8,9,10);  

问题思考

人为选择

这三条QUERY,实际上有三重含义:

1、gid=123的行根本不存在。

如果让你来选索引,你肯定会选复合索引,马上就能定位到数据不存在扫描最少的BLOCK。

select * from tbl where id in (1,2,3,4,5,6,7,8,9,10) and gid=123;  

2、gid=1存在,同时id里面的条件也存在。

如果让你来选索引,应该也是选择复合索引,因为精确定位到了所有的行。

当然如果id in里面很多记录不存在,那么你可能就会选择id单列索引,因为这个索引本身更小,可能扫描更少的BLOCK。

select * from tbl where id in (1,2,3,4,5,6,7,8,9,10) and gid=1;  

3、只有id的条件。

此时,肯定选单列索引了。

select * from tbl where id in (1,2,3,4,5,6,7,8,9,10);  

实际情况如何呢?

1、数据库执行计划与预期一致

postgres=# explain (analyze,verbose,timing,costs,buffers) select * from tbl where id in (1,2,3,4,5,6,7,8,9,10) and gid=123;  
                                                      QUERY PLAN                                                        
----------------------------------------------------------------------------------------------------------------------  
 Index Only Scan using idx2 on public.tbl  (cost=0.43..2.46 rows=1 width=8) (actual time=0.037..0.037 rows=0 loops=1)  
   Output: id, gid  
   Index Cond: (tbl.gid = 123)  
   Filter: (tbl.id = ANY ('{1,2,3,4,5,6,7,8,9,10}'::integer[]))  
   Heap Fetches: 0  
   Buffers: shared hit=3  
 Planning time: 0.829 ms  
 Execution time: 0.086 ms  
(8 rows)  

2、与预期一致

postgres=# explain (analyze,verbose,timing,costs,buffers) select * from tbl where id in (1,2,3,4,5,6,7,8,9,10) and gid=1;  
                                                      QUERY PLAN                                                         
-----------------------------------------------------------------------------------------------------------------------  
 Index Only Scan using idx2 on public.tbl  (cost=0.43..15.46 rows=1 width=8) (actual time=0.026..0.037 rows=2 loops=1)  
   Output: id, gid  
   Index Cond: ((tbl.gid = 1) AND (tbl.id = ANY ('{1,2,3,4,5,6,7,8,9,10}'::integer[])))  
   Heap Fetches: 2  
   Buffers: shared hit=31  
 Planning time: 0.121 ms  
 Execution time: 0.058 ms  
(7 rows)  

3、与预期一致

postgres=# explain (analyze,verbose,timing,costs,buffers) select * from tbl where id in (1,2,3,4,5,6,7,8,9,10);  
                                                     QUERY PLAN                                                       
--------------------------------------------------------------------------------------------------------------------  
 Index Scan using idx1 on public.tbl  (cost=0.43..15.52 rows=10 width=8) (actual time=0.021..0.035 rows=10 loops=1)  
   Output: id, gid  
   Index Cond: (tbl.id = ANY ('{1,2,3,4,5,6,7,8,9,10}'::integer[]))  
   Buffers: shared hit=31  
 Planning time: 0.104 ms  
 Execution time: 0.055 ms  
(6 rows)  

问题升华

数据库生成执行计划靠的是统计信息,如果统计信息不准确,那么执行计划必然不准确。

例如我们人为关闭TBL的自动统计信息收集,然后写入一批新的数据。

postgres=# alter table tbl set (autovacuum_enabled =off);  
ALTER TABLE  
postgres=# insert into tbl select generate_series(1,10000000), 100;  
INSERT 0 10000000  

这个数据的特点是GID=100,在原有的统计信息中,gid=100的行是不存在的,所以下面的SQL优化器显然做出了错误的决定。

postgres=# explain (analyze,verbose,timing,costs,buffers) select * from tbl where id in (1,2,3,4,5,6,7,8,9,10) and gid=100;  
                                                        QUERY PLAN                                                          
--------------------------------------------------------------------------------------------------------------------------  
 Index Only Scan using idx2 on public.tbl  (cost=0.44..2.46 rows=1 width=8) (actual time=0.030..2051.851 rows=10 loops=1)  
   Output: id, gid  
   Index Cond: (tbl.gid = 100)  
   Filter: (tbl.id = ANY ('{1,2,3,4,5,6,7,8,9,10}'::integer[]))  
   Rows Removed by Filter: 9999990  
   Heap Fetches: 10000000  
   Buffers: shared hit=71574  
 Planning time: 0.130 ms  
 Execution time: 2051.900 ms  
(9 rows)  

更新统计信息后,执行计划就准确了。

postgres=# analyze tbl;  
ANALYZE  
postgres=# explain (analyze,verbose,timing,costs,buffers) select * from tbl where id in (1,2,3,4,5,6,7,8,9,10) and gid=100;  
                                                       QUERY PLAN                                                          
-------------------------------------------------------------------------------------------------------------------------  
 Index Only Scan using idx2 on public.tbl  (cost=0.44..20.57 rows=10 width=8) (actual time=0.027..0.043 rows=10 loops=1)  
   Output: id, gid  
   Index Cond: ((tbl.gid = 100) AND (tbl.id = ANY ('{1,2,3,4,5,6,7,8,9,10}'::integer[])))  
   Heap Fetches: 10  
   Buffers: shared hit=31  
 Planning time: 0.212 ms  
 Execution time: 0.067 ms  
(7 rows)  

如何自动收集统计信息

开启autovacuum , track_counts即可。

有几个微调参数,决定了什么时候扫描是否需要收集统计信息,以及当前表的变化量。

track_counts = on  
  
#------------------------------------------------------------------------------  
# AUTOVACUUM PARAMETERS  
#------------------------------------------------------------------------------  
  
autovacuum = on                 # Enable autovacuum subprocess?  'on'  
                                        # requires track_counts to also be on.  
#log_autovacuum_min_duration = -1       # -1 disables, 0 logs all actions and  
                                        # their durations, > 0 logs only  
                                        # actions running at least this number  
                                        # of milliseconds.  
#autovacuum_max_workers = 3             # max number of autovacuum subprocesses  
                                        # (change requires restart)  
autovacuum_naptime = 3s         # time between autovacuum runs  
#autovacuum_vacuum_threshold = 50       # min number of row updates before  
                                        # vacuum  
#autovacuum_analyze_threshold = 50      # min number of row updates before  
                                        # analyze  
#autovacuum_vacuum_scale_factor = 0.2   # fraction of table size before vacuum  
#autovacuum_analyze_scale_factor = 0.1  # fraction of table size before analyze  
#autovacuum_freeze_max_age = 200000000  # maximum XID age before forced vacuum  
                                        # (change requires restart)  
#autovacuum_multixact_freeze_max_age = 400000000        # maximum multixact age  
                                        # before forced vacuum  
                                        # (change requires restart)  
autovacuum_vacuum_cost_delay = 0ms      # default vacuum cost delay for  
                                        # autovacuum, in milliseconds;  
                                        # -1 means use vacuum_cost_delay  
#autovacuum_vacuum_cost_limit = -1      # default vacuum cost limit for  
                                        # autovacuum, -1 means use  
                                        # vacuum_cost_limit  

PostgreSQL优化器是支持CBO与遗传算法

《数据库优化器原理 - 如何治疗选择综合症》

评估每个条件过滤多少行

《PostgreSQL pg_stats used to estimate top N freps values and explain rows》

统计信息解读

《PostgreSQL pg_stat_ pg_statio_ 统计信息(scan,read,fetch,hit)源码解读》

《PostgreSQL 统计信息pg_statistic格式及导入导出dump_stat - 兼容Oracle》

升华-多列统计信息

《PostgreSQL 10 黑科技 - 自定义统计信息》

其他因统计信息不准导致的性能问题

《Greenplum 统计信息收集参数 - 暨统计信息不准引入的broadcast motion一例》

其他参考文献

《PostgreSQL 10 黑科技 - 自定义统计信息》

《数据库优化器原理 - 如何治疗选择综合症》

《PostgreSQL bitmapAnd, bitmapOr, bitmap index scan, bitmap heap scan》

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

《Greenplum 统计信息收集参数 - 暨统计信息不准引入的broadcast motion一例》

《PostgreSQL pg_stat_ pg_statio_ 统计信息(scan,read,fetch,hit)源码解读》

《PostgreSQL 统计信息pg_statistic格式及导入导出dump_stat - 兼容Oracle》

《PostgreSQL pg_stats used to estimate top N freps values and explain rows》

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍如何基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
目录
相关文章
|
4月前
|
存储 监控 算法
电脑监控管理中的 C# 哈希表进程资源索引算法
哈希表凭借O(1)查询效率、动态增删性能及低内存开销,适配电脑监控系统对进程资源数据的实时索引需求。通过定制哈希函数与链地址法冲突解决,实现高效进程状态追踪与异常预警。
250 10
|
4月前
|
算法 数据可视化 测试技术
HNSW算法实战:用分层图索引替换k-NN暴力搜索
HNSW是一种高效向量检索算法,通过分层图结构实现近似最近邻的对数时间搜索,显著降低查询延迟。相比暴力搜索,它在保持高召回率的同时,将性能提升数十倍,广泛应用于大规模RAG系统。
419 10
HNSW算法实战:用分层图索引替换k-NN暴力搜索
|
8月前
|
存储 监控 关系型数据库
B-tree不是万能药:PostgreSQL索引失效的7种高频场景与破解方案
在PostgreSQL优化实践中,B-tree索引虽承担了80%以上的查询加速任务,但因多种原因可能导致索引失效,引发性能骤降。本文深入剖析7种高频失效场景,包括隐式类型转换、函数包裹列、前导通配符等,并通过实战案例揭示问题本质,提供生产验证的解决方案。同时,总结索引使用决策矩阵与关键原则,助你让索引真正发挥作用。
526 0
|
8月前
|
SQL 存储 关系型数据库
PostgreSQL窗口函数避坑指南:如何让复杂分析查询提速300%?
本文基于真实企业级案例,深入剖析PostgreSQL窗口函数的执行原理与性能陷阱,提供8大优化策略。通过定制索引、分区裁剪、内存调优及并行处理等手段,将分钟级查询压缩至秒级响应。结合CTE分阶段计算与物化视图技术,解决海量数据分析中的瓶颈问题。某金融客户实践表明,风险分析查询从47秒降至0.8秒,效率提升5800%。文章附带代码均在PostgreSQL 15中验证,助您高效优化SQL性能。
415 0
|
4月前
|
存储 机器学习/深度学习 监控
网络管理监控软件的 C# 区间树性能阈值查询算法
针对网络管理监控软件的高效区间查询需求,本文提出基于区间树的优化方案。传统线性遍历效率低,10万条数据查询超800ms,难以满足实时性要求。区间树以平衡二叉搜索树结构,结合节点最大值剪枝策略,将查询复杂度从O(N)降至O(logN+K),显著提升性能。通过C#实现,支持按指标类型分组建树、增量插入与多维度联合查询,在10万记录下查询耗时仅约2.8ms,内存占用降低35%。测试表明,该方案有效解决高负载场景下的响应延迟问题,助力管理员快速定位异常设备,提升运维效率与系统稳定性。
276 4
|
10月前
|
存储 算法 C++
Windows共享文件:探秘C++实现的B树索引算法奇境
在数字化时代,Windows共享文件的高效管理至关重要。B树算法以其自平衡多路搜索特性,在文件索引与存储优化中表现出色。本文探讨B树在Windows共享文件中的应用,通过C++实现具体代码,展示其构建文件索引、优化数据存储的能力,提升文件检索效率。B树通过减少磁盘I/O操作,确保查询高效,为企业和个人提供流畅的文件共享体验。
|
5月前
|
存储 监控 算法
企业电脑监控系统中基于 Go 语言的跳表结构设备数据索引算法研究
本文介绍基于Go语言的跳表算法在企业电脑监控系统中的应用,通过多层索引结构将数据查询、插入、删除操作优化至O(log n),显著提升海量设备数据管理效率,解决传统链表查询延迟问题,实现高效设备状态定位与异常筛选。
160 3
|
7月前
|
存储 监控 算法
基于 Python 跳表算法的局域网网络监控软件动态数据索引优化策略研究
局域网网络监控软件需高效处理终端行为数据,跳表作为一种基于概率平衡的动态数据结构,具备高效的插入、删除与查询性能(平均时间复杂度为O(log n)),适用于高频数据写入和随机查询场景。本文深入解析跳表原理,探讨其在局域网监控中的适配性,并提供基于Python的完整实现方案,优化终端会话管理,提升系统响应性能。
211 4
|
监控 关系型数据库 数据库
PostgreSQL的索引优化策略?
【8月更文挑战第26天】PostgreSQL的索引优化策略?
563 1
|
12月前
|
SQL 关系型数据库 OLAP
云原生数据仓库AnalyticDB PostgreSQL同一个SQL可以实现向量索引、全文索引GIN、普通索引BTREE混合查询,简化业务实现逻辑、提升查询性能
本文档介绍了如何在AnalyticDB for PostgreSQL中创建表、向量索引及混合检索的实现步骤。主要内容包括:创建`articles`表并设置向量存储格式,创建ANN向量索引,为表增加`username`和`time`列,建立BTREE索引和GIN全文检索索引,并展示了查询结果。参考文档提供了详细的SQL语句和配置说明。
450 2

相关产品

  • 云原生数据库 PolarDB
  • 云数据库 RDS PostgreSQL 版
  • 推荐镜像

    更多