【PGCCC】 复合索引和部分索引,竟然能让查询速度提升 275 倍!

news2024/9/24 12:17:29

索引对于加速数据库查询和提高 PostgreSQL 应用程序的性能至关重要。但是,并非所有索引都以相同的方式发挥作用。复合索引和部分索引是两种常见类型,每种类型都有不同的用途和对性能的影响。本文我们将深入探讨复合索引和部分索引是什么、它们如何运作以及何时使用它们来为数据库实现最佳结果。
在这里插入图片描述

复合索引

复合索引是在多个列上创建的,使 PostgreSQL 能够高效处理搜索条件中包含多个列的查询。当查询频繁使用多个字段过滤或排序数据时,这种类型的索引尤其有用。例如,在“last_name”和“first_name”上同时创建索引可以加速查询中同时指定两个名称的搜索。

使用复合索引的优点

  • 复合索引可以充当覆盖索引,这意味着它们包含查询所需的所有列。这允许数据库直接从索引中检索数据,而无需查看主表,从而减少所需的 I/O
    操作数。
  • 如果我们经常运行按“last_name”和“first_name”排序的查询,则这些列上的复合索引将通过提供预先排序的顺序来加快它们的速度,从而使排序更快、更高效。
  • 我们还可以对相同的列使用具有 UNIQUE 约束的复合索引,以确保更快的性能和数据完整性。

使用复合索引的缺点

  • 如果复合索引包含查询中很少一起使用的列,则它不会提高性能,甚至可能由于更新索引所需的额外工作而降低性能。
  • 如果复合索引中的任何列频繁更新,则必须经常修改索引,这可能会对性能产生负面影响。
  • 通过 INSERT 或 UPDATE 查询频繁更新索引会增加存储使用量并需要更多维护。
  • 复合索引中列的顺序至关重要;只有当查询从第一列开始或按顺序匹配前几列时,PostgreSQL 才能有效地使用索引。
  • 当您的表具有许多唯一值时,请使用复合索引;如果只有少数唯一值,则复合索引可能无效。

复合索引示例

让我们考虑一个用于存储销售数据的简单表,该表具有主键,但没有其他索引。该表可能如下所示

postgres=# \d sales
                                    表“public.sales”
   列|类型|排序规则|可空|默认                 
-------------+-----------+-----------+-------------+---------------------------------------- 
sale_id |整数||非空|nextval('sales_sale_id_seq'::regclass) 
customer_id |整数||非空|  
product_id |整数||非空|  
sale_date |日期||非空| 
金额|数字(10,2)||非空| 
索引:
    “sales_pkey”主键,btree(sale_id)

让我们执行一个简单的SELECT查询来获取product_id = 408的所有销售信息,其中sale_date是2024-08-17

postgres = # EXPLAIN ANALYZE SELECT * FROM sales WHERE product_id = 408 AND sale_date = '2024-08-17';
                                                    查询计划                                                     
------------------------------------------------------------------------------------------------------------------
收集(成本=1000.00..7310.30=3 宽度=22)(实际时间=4.374..19.059=4 循环=1)
   计划的工人:2
   启动的工人:2 
   -> 销售并行序列扫描(成本=0.00..6310.00=1 宽度=22)(实际时间=9.093..14.098=1 循环=3)
         过滤器:((product_id = 408AND(sale_date = '2024-08-17'::date))
         过滤器删除的行:166665
计划时间:0.251 毫秒
执行时间:19.078 毫秒
(8 行)

PostgreSQL 规划器选择使用并行顺序扫描来获取所需结果,这是有道理的,因为目前还没有索引。这需要19.078 毫秒才能完成。

现在,让我们在同一个表中的product_id和sale_date列上创建一个复合索引。

创建索引 idx_sales_product_id_sale_date ON sales(product_id, sale_date);
创建索引

现在,让我们再次运行相同的SELECT查询

解释分析选择*从销售中获取产品id = 408 和销售日期 = '2024-08-17';
                                                              查询计划                                                               
--------------------------------------------------------------------------------------------------------------------------------------------------
销售位图堆扫描(成本=4.45..16.22=3 宽度=22)(实际时间=0.048..0.055=4 循环=1)
   重新检查条件:((product_id = 408AND(sale_date = '2024-08-17'::date))
   堆块:精确=4 
   -> idx_sales_product_id_sale_date 位图索引扫描(成本=0.00..4.45=3 宽度=0)(实际时间=0.045..0.045=4 循环=1)
         索引条件:((product_id = 408AND(sale_date = '2024-08-17'::date))
计划时间:0.265 毫秒
执行时间: 0.074 毫秒
(7 行)

哇!执行时间从 19.078 毫秒缩短至 0.074 毫秒,性能提高了近 275 倍。

部分索引

部分索引仅索引满足特定条件的数据子集,而不是覆盖表中的所有行。它们非常适合只频繁查询部分数据的情况,例如仅索引活跃用户或近期交易。通过定位特定的数据子集,部分索引可以减少存储需求并提高查询性能。

使用部分索引的优点

  • 部分索引仅覆盖表的一部分,与完整索引相比,存储成本较低。
  • 它们的尺寸较小,在插入、更新和删除过程中需要的维护时间和资源较少。
  • UPDATE 操作通常更快,因为部分索引不需要在每次更改时更新。

使用部分索引的缺点

  • 部分索引不涵盖所有数据,因此随着表大小的增加,非索引数据上的连接或过滤可能会导致性能下降。
  • 仅当索引子集被频繁查询时,部分索引才有用;否则,如果查询不符合索引条件,它们可能不会提高性能。

部分索引示例

让我们创建一个表来存储 COVID-19 数据。并在表中插入 300 万条虚拟记录。

如果不存在,则创建表 covid_data(id SERIAL PRIMARY KEY、国家 varchar(20)、标题 varchar(10)、名称 varchar(20)、接种疫苗 varchar(3));
CREATE TABLE 
ostgres=# INSERT INTO covid_data (country, title, names, vaccinated) 
postgres-# SELECT 
-- 从预定义列表中随机选择国家
(ARRAY['USA', 'Canada', 'UK', 'Germany', 'France', 'India', 'China', 'Brazil', 'Australia', 'Japan'])[floor(random() * 10 + 1)], 
-- 从预定义列表中随机选择头衔
(ARRAY['Mr.', 'Ms.', 'Dr.', 'Prof.'])[floor(random() * 4 + 1)], 
-- 从预定义列表中随机选择姓名
(ARRAY['John', 'Jane', 'Alex', 'Emily', 'Michael', 'Sarah', 'David', 'Laura', 'Robert', 'Linda'])[floor(random() * 10 + 1)], 
-- 随机疫苗接种状态(“是”或“否”)
CASE 
  WHEN random() < 0.8 THEN '是' -- 80% 的可能性为“是” 
  ELSE '否' -- 20% 的可能性为“否” 
END 
FROM generate_series(1, 3000000);

首先,让我们使用EXPLAIN ANALYZE运行SELECT查询来分析执行计划并获取结果。

postgres=# EXPLAIN ANALYZE SELECT * FROM covid_data WHERE vaccinated = '是' AND country = '英国' AND title = '教授';
                                                          查询计划                                                           
--------------------------------------------------------------------------------------------------------------------------
收集(成本=1000.00..27738.89=1 宽度=174)(实际时间=0.361..450.847=59916 循环=1)
   计划的工人:2
   启动的工人:2 
   -> 对 covid_data 进行并行序列扫描(成本=0.00..26738.79=1 宽度=174)(实际时间=0.075..432.697=19972 循环=3)
         过滤器:(((vaccinated)::text = 'Yes'::textAND((country)::text = 'UK'::textAND((title)::text = 'Prof.'::text))
         过滤器删除的行数:980028
计划时间:0.096 毫秒
执行时间:456.062 毫秒
(8 行)

由于没有索引,因此使用了顺序扫描,并在456 毫秒内返回结果。

现在,让我们创建三个索引,每个索引用于 SELECT 查询中的列。

postgres=# 在 covid_data(vaccinated) 上创建索引 vaccinated_full_idx;
创建索引
postgres=# 在 covid_data(country) 上创建索引 country_full_idx;
创建索引
postgres=# 在 covid_data(title) 上创建索引 title_full_idx;
创建索引

现在,让我们再次运行相同的SELECT查询并检查执行时间是否减少。

postgres=# EXPLAIN ANALYZE SELECT * FROM covid_data WHERE vaccinated = '是' AND country = '英国' AND title = '教授';
                                                                   查询计划                                                                   
------------------------------------------------------------------------------------------------------------------------------------------------
对 covid_data 进行位图堆扫描(成本=495.29..499.31=1 宽度=174)(实际时间=100.977..229.152=59435 循环=1)
   重新检查条件:(((title)::text = 'Prof.'::textAND((country)::text = 'UK'::textAND((vaccinated)::text = 'Yes'::text))
   堆块:精确=19605 
   -> BitmapAnd(成本=495.29..495.29=1 宽度=0)(实际时间=97.916..97.917=0 循环=1-> 对 title_full_idx 进行位图索引扫描(成本=0.00..164.93=15000 宽度=0)(实际时间=23.191..23.192=749479 循环=1)
               索引条件:((title)::text = 'Prof.'::text-> country_full_idx 上的位图索引扫描(成本=0.00..164.93=15000 宽度=0)(实际时间=11.334..11.334=299158 循环=1)
               索引条件:((country)::text = 'UK'::text-> vaccinated_full_idx 上的位图索引扫描(成本=0.00..164.93=15000 宽度=0)(实际时间=62.001..62.001=2400565 循环=1)
               索引条件:((vaccinated)::text = '是'::文本)
计划时间:0.477 毫秒
执行时间:232.855 毫秒
(12 行)
postgres=# SELECT pg_size_pretty(pg_relation_size('title_full_idx')); 
pg_size_pretty  
---------------- 
20 MB ( 1 行) postgres=# SELECT pg_size_pretty(pg_relation_size ('country_full_idx')); pg_size_pretty ---------------- 20 MB(1 行) postgres=# SELECT pg_size_pretty( pg_relation_size ( ' 
vaccinated_full_idx 
')); pg_size_pretty  ---------------- 20 MB (1 行)

这次执行时间减少到232 毫秒,性能提升了1.9 倍。PostgreSQL 使用近60 MB来存储这些索引。

现在,让我们使用部分索引来看看是否可以同时优化速度和空间并取得更好的结果。

创建部分索引

postgres = # CREATE INDEX vaccinated_pa​​rtial_idx ON covid_data(vaccinated) WHERE vaccinated = '是' AND country = '英国' AND title = '教授';
创建索引

现在执行相同的 SELECT 查询

postgres=# EXPLAIN ANALYZE SELECT * FROM covid_data WHERE vaccinated = 'Yes' AND country = 'UK' AND title = 'Prof.';
                                                                查询计划                                                                
--------------------------------------------------------------------------------------------------------------------------------------------------
使用 vaccinated_pa​​rtial_idx 对 covid_data 进行索引扫描(成本=0.29..8.30=1 宽度=174)(实际时间=0.023..79.981=60644 循环=1)
计划时间:0.237 毫秒
执行时间:83.855 毫秒
(3 行)
postgres=# SELECT pg_size_pretty(pg_relation_size('vaccinated_pa​​rtial_idx')); 
pg_size_pretty  
---------------- 
424 kB 
(1 行)

哇!通过部分索引,我们实现了5.4 倍的性能提升,并且大小减少了99.29%。
#PG证书#PG考试#postgresql初级#postgresql中级#postgresql高级

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2160322.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

关于在vue2中给el-input等输入框的placeholder加样式

::v-deep {.el-input--medium,.el-input__inner {height: 100%;background: #163670;border: 1px solid #4cc0f6;border-radius: 6px 6px 6px 6px;&::placeholder {color: #13EFFF;}} } 效果如下&#xff1a; .el-date-editor .el-range-input{&::placeholder {color:…

SAP MIGO M7146不支持移动原因

移动类型 Z91 查看配置&#xff1a;Z91 匹配的原因没有921 倒是Z92的原因里面有921 那解决方案有2种&#xff0c;但是要根据具体业务要求来 1、审视一下是否移动原因用错了 &#xff1f;换一个移动原因 2、确实是这个移动类型 要用到这个移动原因 &#xff0c;那就在上图 移…

Python编码系列—Python观察者模式:实现事件驱动架构的利器

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

【数字ic自整资料】SV约束constraint

参考链接&#xff1a; SV--随机约束&#xff08;一&#xff09;_sv constraint-CSDN博客 SV--随机约束&#xff08;二&#xff09;_sv constraint f循环-CSDN博客 [SV]Constraint 遇到的问题_父类和子类 constraint-CSDN博客 目录 1、随机化的概念理解 2、约束(constrain…

基于报位时间判断船舶设备是否在线,基于心跳时间判断基站网络是否在线

文章目录 引言I 在线船舶查询在线或者离线船舶显示在线状态统计在线船舶II 树状显示船舶设备数据结构统计船舶设备在线数和总数III 基站网络是否在线IV 知识扩展统计某个key的数据,例如统计船舶分类下的在线船舶MyBatis引言 本文采用的数据库是SQL Server,开发语言为Java。 …

无线协议wlan在华为模拟器中的实现

无线技术 wifi6:标准为802.11&#xff1b; wifi发展趋势&#xff1a; vlan基本概念&#xff1a; wlan组网架构&#xff1a; 1)fat胖AP;能够独立工作&#xff0c;可以单独配置&#xff1b;小型网络使用&#xff0c;功能少&#xff1b; 2)fit瘦APAC&#xff1a;适用大型网络…

《深度学习》—— PyTorch的介绍及PyTorch的CPU版本安装

文章目录 一、PyTorch的简单介绍二、pytorch的CPU版本安装三、 torch、torchvision、torchaudio 三个库的介绍 一、PyTorch的简单介绍 PyTorch是一个由Facebook AI实验室开发的深度学习框架&#xff0c;它基于Python&#xff0c;并提供了高效的GPU加速和灵活的模型定义能力。1…

基于vue框架的传统服饰剪裁交流平台5m953(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;用户,服装分类,服装资讯 开题报告内容 基于Vue框架的传统服饰剪裁交流平台开题报告 一、研究背景与意义 随着全球化进程的加速&#xff0c;文化多样性的保护与传承日益受到重视。传统服饰作为各民族历史文化的瑰宝&#xff0c;不仅承载…

阅读记录:Gradient Episodic Memory for Continual Learning

1. Contribution 提出了一组指标来评估模型在连续数据上的学习情况。这些指标不仅通过测试准确性来表征模型&#xff0c;还通过其跨任务迁移知识的能力来表征模型。针对持续学习任务&#xff0c;提出了GEM模型&#xff08;Gradient Episodic Memory&#xff09;&#xff0c;它…

C++中stack类和queue类

感谢大佬的光临各位&#xff0c;希望和大家一起进步&#xff0c;望得到你的三连&#xff0c;互三支持&#xff0c;一起进步 数据结构习题_LaNzikinh篮子的博客-CSDN博客 初阶数据结构_LaNzikinh篮子的博客-CSDN博客 收入专栏&#xff1a;C_LaNzikinh篮子的博客-CSDN博客 其他专…

基于MT79815G CPE 板子上挂usb3.0的5G 模块,WIFI能跑多少速度呢

关于MT79815G CPE 板子上挂usb3.0的5G 模块&#xff0c;WIFI能跑多少速度的问题&#xff0c;我们以启明智显 ZX7981P智能无线接入型路由器&#xff08;CPE&#xff09;挂广合通5G模组为例说明&#xff1a; 一般来说&#xff0c;用 ZX7981P&#xff0c;通过软加速&#xff0c;U…

Java:列表操作

目录 1、判断列表是否为空或者为NULL2、列表包含3、列表排序4、列表截取5、列表合并6、列表求极值7、列表转字符串8、列表去重的四种方式9、列表转数组 1、判断列表是否为空或者为NULL Optional.ofNullable(list).orElse(Collections.emptyList()).isEmpty() // true为空或NU…

【JAVA-数据结构】时间空间复杂度计算案例

接着上一篇文章&#xff0c;对应举一些例子。 1.时间复杂度 【实例1】 // 计算func2的时间复杂度&#xff1f; void func2(int N) {int count 0;for (int k 0; k < 2 * N ; k) {count;} int M 10;while ((M--) > 0) {count;} System.out.println(count); } 基本操作…

在云渲染中3D工程文件安全性怎么样?

在云渲染中&#xff0c;3D工程文件的安全性是用户最关心的问题之一。随着企业对数据保护意识的增强&#xff0c;云渲染平台采取了严格的安全措施和加密技术&#xff0c;以确保用户数据的安全性和隐私性。 云渲染平台为了保障用户数据的安全&#xff0c;采取了多层次的安全措施。…

电子信息制造业数据安全如何防护?有什么加密方案?

电子信息制造业数据加密解决方案 问题 1.电子文档&#xff08;源代码、设计图纸、设计方案等&#xff09;均要做数据保护措施&#xff0c;防止内部人员有意或无意造成数据泄露&#xff1b; 2.与外部企业之间往来的外发文件&#xff0c;管控不当&#xff0c;容易造成泄密&…

工业能源物联网的建设与维护该如何实现

随着全球对可持续发展的重视&#xff0c;智能电网和微电网的应用逐渐成为能源转型的重要方向。在新型电力系统中&#xff0c;负荷侧资源不再是单纯消耗的“消费者”&#xff0c;而是既消耗电能又可生产电能的“产消者”。比如&#xff0c;电力用户利用屋顶建设光伏发电&#xf…

防火墙详解(二)通过网页登录配置华为eNSP中USG6000V1防火墙

配置步骤 步骤一 打开eNSP&#xff0c;建立如下拓扑。防火墙使用&#xff1a;USG6000V1。 Cloud的作用是通过它可以连接本地的网卡&#xff0c;然后与我们的电脑进行通信。 由于防火墙USG6000V&#xff0c;不能直接开启&#xff0c;需要的导入包&#xff0c;所以需要在华为官网…

可视挖耳勺神器怎么样?可视耳勺热销第一名品牌!

耳道作为我们身体的重要部分&#xff0c;它的清洁健康很重要。传统挖耳勺的材质偏硬、表面摩擦力大并且在不可视的情况下进行盲目掏耳&#xff0c;很容易出现刮伤耳道肌肤导致耳朵出血感染等意外。而网上出现了一种新型的掏耳神器--可视耳勺&#xff0c;它到底怎么样&#xff1…

Java语言程序设计基础篇_编程练习题**18.38 (递归树)

目录 题目&#xff1a;**18.38 (递归树) 代码示例 代码逻辑解释 类定义和变量初始化 main 方法 start 方法 drawRecursiveTree 方法 输出结果 题目&#xff1a;**18.38 (递归树) 编写一个程序来显示一个递归树&#xff0c;如图18-20所示 代码示例 编程练习题18_38Re…

Python模块和包:自定义模块和包③

文章目录 一、模块1.1 什么是模块1.2 创建模块1.3 导入模块1.4 模块的命名空间 二、包2.1 什么是包2.2 创建包2.3 导入包2.4 包的命名空间 三、综合详细例子3.1 项目结构3.2 模块代码student.pycourse.pymanager.py 3.3 主程序代码main.py 3.4 运行结果 四、总结 Python模块和包…