掌握查询利器深入理解postgresql索引原理与优化(代码片段)

陈书予 陈书予     2023-04-03     571

关键词:

文章目录


一、前言

1.1 PostgreSQL索引的重要性

PostgreSQL索引是数据库性能优化中很重要的一个方向,合理使用索引可以有效提升数据库的查询效率。在PostgreSQL中,索引机制是为了更快地寻找某个值而构建的。PostgreSQL索引可加速搜索操作,当数据表中的数据行太多时,没有索引会影响性能,影响查询速度,而索引能够提供快速查找数据的能力,除了基础索引外,还有B树索引、哈希索引、GiST索引、GIN索引等,各种索引类型的执行效率不同,如果我们使用恰当的索引,可以显著提高数据库表数据的查询效率。

1.2 本文的结构和目的

本文从索引的概念、类型、实践、优化及使用场景入手,力图帮助读者进一步了解 PostgreSQL 索引,更好地使用索引优化查询性能,提高数据库的性能。

二、索引概述

2.1 什么是索引

在数据库中,索引被定义为一种数据结构,它可以提供快速的数据查找方式。简单来说,索引是一种能够极大提高数据库查询效率的结构,类似于字典,其中包含了键值(key)和指向数据的指针或者物理地址。通过从索引中查找关键字,可以帮助我们快速访问数据库表格。

在 PostgreSQL 中,索引是建立在某个数据库表格的一列或者多列上的一种单独的数据结构。这些列通常是一些需要经常用来进行查询和排序的列。通过创建索引,可以让查询器更快的获取到需要的数据,从而提高查询的性能。

2.2 为什么要使用索引

当数据库中包含大量数据时,使用索引会带来巨大的查询性能提升。如果没有索引,查询语句可能要扫描所有数据,与完整的数据集交互多次,这往往会耗费很多时间。而拥有正确的索引之后,查询就可以只检索表格中的索引列,而不是整张表中的所有数据。这样就可以快速定位到需要查找的数据所在的位置,节省了很多查询时间。

总之,使用索引会显著提高数据库的性能和响应时间,尤其是对于大数据集的查询。

2.3 索引如何工作

当我们在 PostgreSQL 中创建索引时,系统依据我们定义时给出的列和索引类型在内存中建立一个数据结构,用于存储列值和列值对应的物理地址之间的映射关系。当用户使用查询语句时,PostgreSQL会首先查询索引,按照我们定义时的方式排序查询结果,然后再使用这个索引中的物理地址直接访问数据。这样,查询就可以直接定位到有用数据的物理位置,不需要扫描整张表,从而可以大幅缩短查询时间。

不过,需要注意的是,索引也有一定的开销。当我们创建了一个索引后,每次插入、更新或删除该列所在的表格时,都会需要更新索引。因此,我们需要根据具体情况考虑何时使用索引以及使用哪些类型的索引。

三、索引类型

3.1 B-tree索引

B-tree索引是最常用的索引类型之一,它是一种平衡树结构,可以有效地支持范围查询、排序和模糊查询等操作。在 PostgreSQL 中,B-tree 索引适用于大多数场景,包括整数、字符串、日期等数据类型的索引

下面是一个简单的例子,展示了如何在 PostgreSQL 中创建 B-tree 索引。我们创建一个名为 scores 的表,其中包含学生姓名和成绩两列:

CREATE TABLE scores (
    name VARCHAR(50) NOT NULL,
    score INTEGER NOT NULL
);

现在我们想在 name 这一列上创建一个 B-tree 索引,可以使用如下语句:

CREATE INDEX idx_scores_name ON scores USING btree (name);

在这里,idx_scores_name 是索引的名称,scores 是表名,name 是要索引的列名,btree 是索引类型。

在创建了索引之后,我们可以使用 EXPLAIN 命令查看它是否被正确使用。例如,我们可以查询所有成绩在 80 分到 90 分之间的学生:

EXPLAIN
SELECT name, score
FROM scores
WHERE score BETWEEN 80 AND 90;

如果索引被正确使用,我们应该能看到查询计划中包含一个 Index Scan 步骤。

3.2 Hash索引

与 B-tree 索引不同,Hash 索引只支持等值匹配的查询。如果查询条件包含任何不等于号、小于号或大于号,就无法使用 Hash 索引。Hash 索引通常适用于较小的表,因为生成 Hash 桶需要耗费较多时间。在 PostgreSQL 中,Hash 索引适用于数值类型和字符串类型的索引。下面是一个使用 Hash 索引的简单例子:

CREATE INDEX idx_scores_hash ON scores USING hash (name);

在这里,idx_scores_name 是索引的名称,scores 是表名,name 是要索引的列名,hash 是索引类型。

3.3 GiST索引

GiST 索引是一种通用的、可扩展的索引类型,支持多种不同的数据类型和查询操作,包括全文检索、几何对象、范围对象、最邻近的邻居等等数据类型。在 PostgreSQL 中,GiST 结构被用于处理大部分扩展模块。

下面是一个使用 GiST 索引的简单例子。我们创建一个名为 users 的表,其中包含用户的 ID、姓名和地址信息:

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name VARCHAR(50) NOT NULL,
    address VARCHAR(100) NOT NULL
);

现在我们想在 address 列上创建一个 GiST 索引,可以使用如下语句:

CREATE INDEX idx_users_address ON users USING gist (address);

在创建了索引之后,我们可以使用 EXPLAIN 命令查看它是否被正确使用。例如,我们可以查询距离一个给定地址最近的用户:

EXPLAIN
SELECT id, name, address,
       ST_Distance_Sphere(address::geometry, 'POINT(-73.9567 40.7855)') AS distance
FROM users
ORDER BY address <-> 'POINT(-73.9567 40.7855)'
LIMIT 1;

在这里,ST_Distance_Sphere 和 <-> 是 GiST 索引特有的函数和运算符,用于计算距离和排序。

3.4 GIN索引

GIN 索引是另一种通用的索引类型,支持多种不同的数据类型和查询操作。与 GiST 相比,GIN 的查询速度更快,但创建速度较慢。GIN 索引通常适用于某属性会有大量相同值的数据表示,比如喜欢和不喜欢等分类属性。

下面是一个使用 GIN 索引的简单例子。我们创建一个名为 keywords 的表,其中包含文章的标题和关键词两列:

CREATE TABLE keywords (
    title VARCHAR(100) NOT NULL,
    keywords TEXT[] NOT NULL
);

现在我们想在 keywords 列上创建一个 GIN 索引,可以使用如下语句:

CREATE INDEX idx_keywords_gin ON keywords USING gin (keywords);

在这里,idx_keywords_gin 是索引的名称,keywords 是要索引的列名,gin 是索引类型。

在创建了索引之后,我们可以使用 @@ 运算符搜索包含特定关键字的文章。例如,我们可以搜索所有包含关键字 database 和 PostgreSQL 的文章:

SELECT title
FROM keywords
WHERE keywords @@ ARRAY['database', 'PostgreSQL'];

在这里,@@ 是 GIN 索引特有的运算符,用于搜索包含某个数组元素的行。

四、索引实践

4.1 创建索引

在PostgreSQL中,创建索引可以使用CREATE INDEX语句,可以指定索引的类型、名称、表名称和要索引的列。例如,以下命令可以在名为table_name的表上创建名为index_name的B-tree索引:

CREATE INDEX index_name ON table_name (column_name);

为多个列创建组合索引可以减少索引数量并提高联合查询的性能。例如,以下命令可以在名为table_name的表上创建名为index_name的组合B-tree索引:

CREATE INDEX index_name ON table_name (column1, column2, column3);

当创建索引时,需要注意以下几个方面:

  • 索引的名称:需要根据实际情况为索引取一个有意义的名称。
  • 索引的类型:需要根据数据类型和查询情况选择合适的索引类型。
  • 索引的列:需要根据查询频率选择合适的列进行索引。

4.2 索引的使用

在使用索引时,需要使用EXPLAIN语句来了解查询语句的执行计划,以便可以选择合适的索引和优化查询语句。可以使用EXPLAIN VERBOSE命令查看执行计划的详细信息:

EXPLAIN VERBOSE SELECT * FROM table_name WHERE column_name = 100;

此外,还可以查询pg_stat_user_indexes系统视图来查看索引的使用情况,以便根据情况进行索引的优化。以下命令可以查询名为table_name的表的索引信息:

SELECT * FROM pg_stat_user_indexes WHERE relname = 'table_name';

4.3 索引维护

当表的数据发生变化时,索引也需要进行维护,以确保索引的有效性和正确性。在PostgreSQL中,可以使用VACUUMANALYZE命令来重新组织表和更新索引。

例如,以下命令可以使用VACUUM命令重新组织名为table_name的表:

VACUUM table_name;

此外,还可以使用VACUUM ANALYZE命令来同时更新表和索引的统计信息:

VACUUM ANALYZE table_name;

当更新数据时,backends会自动维护索引,以确保索引的正确性和有效性。对于高并发数据库,可以禁用自动维护索引以提高性能。使用以下语句可以禁用自动维护索引:

SET maintenance_work_mem = '0';

通过合理的索引维护,可以更好地保证数据库的正常运行和查询性能。

五、索引优化与使用场景

5.1 索引优化概述

索引是优化数据库查询性能的重要手段之一,但索引的性能、存储和维护也是需要考虑的问题。为了提高索引的性能和效率,我们可以从以下几个方面进行优化:

  • 优化SQL查询:通过调整SQL查询语句、减少不必要的查询以及避免缓存脏数据等方式来优化查询。
  • 优化索引的结构:通过创建不同类型的索引、增加/减少索引的列,以及组合或拆分已有的索引等方式来优化索引的结构。
  • 使用更好的硬件设备:通过增加内存、采用SSD硬盘等方式来提高硬件性能。

5.2 索引优化的工具

PostgreSQL提供了多个工具来帮助用户优化索引,包括:

  • pg_stat_statements:用于分析SQL查询语句的执行情况和性能,以便优化查询和索引。
  • pg_top:用于监控数据库进程和消耗资源的情况,以便优化查询和索引。
  • pgBadger:用于生成可视化的日志分析报告,以帮助用户了解数据库性能和问题出现的原因。

5.3 索引优化的实践

优化索引应始于优化SQL查询。通过分析查询语句的执行计划,查看查询是否使用了索引或者使用了非常差的索引,来调整SQL查询语句。可以使用EXPLAIN关键字来检查查询语句的执行计划,以便找到可能存在的优化方案。

EXPLAIN SELECT * FROM table_name WHERE some_column = 1;

优化索引的结构可以采取增加减少索引的列,以及创建组合拆分已有的索引等方式。例如,创建组合索引可以提高联合查询和排序等操作的性能。

CREATE INDEX index_name ON table_name (column1, column2, column3);

如果发现某个索引效果不佳,可以进行重建、重新组织和重分区等操作,进一步提高索引的性能。

5.4 索引的使用场景

在使用索引时,需要根据查询语句的特点数据量大小查询频率等因素,选择适合的索引类型、创建合理的索引条件,从而提高查询性能。

例如,在进行等值查询时,可以采用B-tree或Hash索引类型,同时采用适当的数据类型来保证索引的正确性和效率;而在进行范围查询时,可以采用B-tree或SP-GiST索引类型来提高索引的效率。

另外,不要在数据量很小的表上强制使用索引,因为这样可能反而会降低查询性能。同时,建议使用独立的表和索引来避免锁和死锁问题。

综上所述,索引优化和使用场景是相互关联的,需要根据具体的需求和情况进行综合考虑,才能更好地提高查询性能并保证数据库的正常运行。

六、索引案例

6.1 基本索引的使用

基本索引一般是指对单个列进行的索引,可以使用B-tree、Hash等索引类型。
为单个列创建B-tree索引:

CREATE INDEX index_name ON table_name (column_name);

为单个列创建Hash索引:

CREATE INDEX index_name ON table_name USING HASH (column_name);

6.2 组合索引的使用

组合索引是指对多个列进行的索引,可以更好地支持联合查询和排序等操作。
多个列创建组合B-tree索引:

CREATE INDEX index_name ON table_name (column1, column2, column3);

注意:组合索引的列顺序很重要,不同的列顺序可能会影响查询性能。

6.3 索引优化实践

可以通过查询pg_stat_user_indexes视图、查看执行计划、使用更佳的数据类型等方法来优化索引。

-- 查询索引使用情况
SELECT idx_stat.indexrelid::regclass AS index_name,
       pg_size_pretty(pg_relation_size(idx_stat.indexrelid)) AS index_size,
       idx_stat.idx_scan,
       idx_stat.idx_tup_read,
       idx_stat.idx_tup_fetch,
       idx_stat.idx_blks_read,
       idx_stat.idx_blks_hit
FROM pg_catalog.pg_indexes AS pg_idx
LEFT JOIN LATERAL (
   SELECT
      pg_stat_get_blocks_fetched(pg_idx.indexrelid) AS idx_blks_read,
      pg_stat_get_blocks_hit(pg_idx.indexrelid) AS idx_blks_hit,
      pg_stat_get_tuples_returned(pg_idx.indexrelid) AS idx_tup_fetch,
      pg_stat_get_tuples_fetched(pg_idx.indexrelid) AS idx_tup_read,
      pg_stat_get_num_scans(pg_idx.indexrelid) AS idx_scan,
      pg_idx.indexrelid
   FROM pg_catalog.pg_stat_all_indexes pg_idx
   WHERE pg_idx.indexrelid = pg_idx.indexrelid
) AS idx_stat ON true
WHERE pg_idx.tablename = 'table_name'
ORDER BY pg_relation_size(idx_stat.indexrelid) DESC;

七、结论

7.1 总结

PostgreSQL是一款功能强大、性能优异的开源关系型数据库管理系统。在实际应用中,高效的查询和数据操作是非常重要的,而索引是提高查询性能的重要手段之一。

索引可以看作是一个有序的数据结构,用于加速数据的检索。通过对表中的某一或某些列创建索引,可以大大提高查询的速度,甚至可以将查询的时间从几秒钟缩短到几毫秒。同时,索引还可以提高表的排序性能和聚合函数的性能等。

在使用索引时,需要根据具体业务场景和查询情况去选择合适的索引类型和优化方法。常见的索引类型包括`B-tree(默认类型)、Hash、GiST和SP-GiST等。其中,B-tree类型适用于等值和范围查询,Hash类型适用于快速等值查询。

除了选择合适的索引类型外,还需要注意索引的列顺序和联合查询的优化。当需要对多个列进行索引时,可以创建组合索引,以提高联合查询和排序等操作的性能。同时,当需要对多个列进行联合查询时,需要按照索引列的顺序进行写查询语句,以避免不必要的扫描和排序操作。

7.2 再次强调索引的重要性

在实际应用中,需要根据数据量的大小、查询频率、SQL语句等因素综合考虑,合理、稳妥地使用和维护索引,从而提高查询性能。根据查询pg_stat_user_indexes视图、查看执行计划、使用更佳的数据类型等方法来优化索引,还可以进一步提高索引的性能和效率。

总之,索引是提高数据库查询性能的重要手段,需要认真、谨慎地处理。通过合理选择索引类型、创建合适的索引和优化查询语句,才能更好地发挥索引的作用,提高查询效率和数据操作效率。

mysql进阶-10深入理解redolog,undolog和binlog的底层原理(代码片段)

...zhenghuishengq/article/details/127889365【六】深入理解mysql的内核查询成本计算https://blog.csdn.net/zhenghuishengq/article/details/128820477【七】深入理解mysql性能优化以及解决慢查询问题https://blog.csdn.net/zhenghuishengq/a 查看详情

深入理解butterknife源码并掌握原理(一)

前言话说在android这座大山里,有一座庙(方块公司-square),庙里住着一个神-jake(我是这么叫的嘻嘻)。不要小看这个小jake,这个神可是为android应用开发们提供了强有力的帮助。比如流行的开源库okhttp,eventbus系列,retrofit... 查看详情

原理+实战快速掌握响应式开发

...。知识点概念/原理与示例结合,让你全面/透彻的理解和掌握响应式开发。2-1单位像素2-2媒体查询-视口12-3媒体查询-视口22-4媒体查询-媒介查询2-5设计稿2-6浮动2-7flex新科技2-8栅格系统2-9分而治之&预处理工具2-10JavaScript2-11调试... 查看详情

深入理解mysql索引底层数据结构与算法(代码片段)

深入理解Mysql索引底层数据结构与算法----->之前很多人还问我一些关于mysql索引的底层和使用,我就特意写一篇文章跟大家一起分享一下我对mysql索引的理解,大家有更深入的理解可以下面留言。1.索引的定义MySQL官方对索引的... 查看详情

elasticsearch学习总结:es介绍与架构说明

...为了更深入地理解ElasticSearch的工作原理,特别是索引和查询这两个过程,理解Lucene的工作原理至关重要。本质上,ElasticSearch是用Lucene来实现索引的查询功能的。1.1定义Lucene是一个成熟的、高性能的、可扩 查看详情

mysql,聚集索引与非聚集索引查询机制原理

原理说明:底层不是用线性,而是通过树构建的我理解的—》聚集索引与非聚集索引:聚集索引innodb聚集名有其意,建表会生成两个表:frm存放表结构的表,ibd(存放索引数据的表)默认大小为96kb... 查看详情

数据库原理实验四——视图与索引(代码片段)

实验目的熟练掌握使用图形用户界面和SQL语言创建、操作和删除视图。熟练掌握使用图形用户界面和SQL语言创建和删除索引。理解与验证索引的作用。实验内容在Student数据库中,用SQL语句创建一个选修了“数据库”课程并且... 查看详情

分布式搜索引擎elasticsearch---elasticsearch进阶使用深入理解搜索技术集群架构原理(代码片段)

ElasticSearchElasticSearchterm查询match查询Elasticsearch架构原理Master节点DataNode节点分片(Shard)副本指定分片、副本数量Elasticsearch重要工作流程Elasticsearch文档写入原理Elasticsearch检索原理Elasticsearch准实时索引实现手工控制搜索结... 查看详情

简单查询语句

...是花费大量时间去学习,对于了解索引原理对于后续理解查询计划和性能调优有很大的帮助,而我们只是一些内容进行概括和总结,这一节我们开始正式步入学习SQL中简单的查询语句,简短的内容,深入的理解。简单查询语句所... 查看详情

请问怎么学习python?

...安排学习计划~第一阶段:专业核心基础阶段目标:1.熟练掌握Python的开发环境与编程核心知识2.熟练运用Python面向对象知识进行程序开发3.对Python的核心库和组件有深入理解4.熟练应用SQL语句进行数据库常用操作5.熟练运用Linux操... 查看详情

深入理解stream之原理剖析

...然是JDK1.8。所以,我们有必要聊一聊Java8的一些新特性。深入理解lambda的奥秘深入理解Stream之原理剖析深入理解Stream之foreach源码解析深入浅出NPE神器Optional谈谈接口默认方法与静态方法深入浅出重复注解与类型注解深入浅出JVM元... 查看详情

深入理解mybatis原理与技术

目录第1章Mybatis简介1.1传统的JDBC编程1.2ORM模型1.4MyBatis1.5什么时候用MyBatis第2章MyBatis入门2.2MyBatis构成2.3SqlSession的作用2.4映射器2.4.1使用XML配置方式2.4.2使用注解的方式2.4.3MyBatis映射原理2.5生命周期2.5.1SqlSessionFactoryBuilder2.5.2SqlSession... 查看详情

理解/优化 Postgresql 中的 SQL 查询

】理解/优化Postgresql中的SQL查询【英文标题】:Understand/OptimizeSQLqueryinPostgresql【发布时间】:2019-12-0419:09:43【问题描述】:所以我有一个查询,我在最坏的情况下运行它,它需要10-12分钟。如果我删除where查询的时间检查,它会下... 查看详情

[转]深入浅出数据库索引原理

...表的服务器端程序员的。然而,会使用索引是一回事,而深入理解索引原理又能恰到好处使用索引又是另一回事,这完全是两个天差地别的境界(我自己也还没有达到这层境界)。很大一部份程序员对索引的了解仅限于到“加 查看详情

数据库索引原理

...。很大一部份程序员对索引的了解仅限于到“加索引能使查询变快”这个概念为止。为什么要给表加上主键?为什么加索引后会使查询变快?为什么加索引后会使写入、 查看详情

深入理解[代理模式]原理与技术(代码片段)

本文原创投稿文作者:像风一样如何理解代理模式?思考抽象问题最好的办法就是具体化!比如我们需要为一个业务方法在执行前后记录日志,为了达到解耦的目的,我们可以再新建一个类并定义一个新的业务方法,该方法既可... 查看详情

深入理解mysql:innodb引擎日志事务索引锁主备调优

....刷脏页的控制策略5.收缩表空间6.count(*)优化7.orderby机制8.查询优化9.短连接风暴10.QPS突增问题11.IO性能瓶颈12.读写分离优化13.误删修复14.join原理与优化explain分析执行计划参考资料逻辑架构阶段注意事项连接器建立连接成本过高&#x... 查看详情

《深入理解mybatis原理4》mybatis缓存机制的设计与实现

《深入理解mybatis原理》MyBatis缓存机制的设计与实现本文主要讲解MyBatis非常棒的缓存机制的设计原理,给读者们介绍一下MyBatis的缓存机制的轮廓,然后会分别针对缓存机制中的方方面面展开讨论。MyBatis将数据缓存设计成两级结... 查看详情