文章 > PostgreSQL > 【技术文章】浅析PostgreSQL索引(1)

【技术文章】浅析PostgreSQL索引(1)

云贝教育 · 2022-05-19 701 PostgreSQL
分享 收藏

作者:崔鹏


索引概述与类型



1、索引概述

  • 系统会在表更新时更新索引,而且会在它觉得使用索引比顺序扫描表效率更高时使用索引

  • 我们可能需要定期地运行ANALYZE命令来更新统计信息以便查询规划器能做出正确的决定

  • 索引也会使带有搜索条件的UPDATE和DELETE命令受益;此外索引还可以在连接搜索中使用

  • 并发构建索引允许并行的写,但是有些警告需要注意

  • 一个索引被创建后,系统必须保持它与表同步。这增加了数据操作的负担

  • 那些很少或从不在查询中使用的索引应该被移除。



2、索引类型

  • PostgreSQL提供了多种索引类型,主要的四种:B-Tree、Hash、GiST和GIN

  • 每种索引使用了不同的算法,都有其适合的查询类型

  • 缺省时,CREATE INDEX命令将创建B-Tree索引


索引的分类及详解




1、B-tree 索引

  • B-Tree索引是标准的索引类型,B代表平衡,主要用于等于和范围查询,具体使用场景:

  • 当索引列包含操作符" <、<=、=、>=和>"作为查询条件时

  • 在使用BETWEEN、IN、IS NULL和IS NOT NULL的查询中

  • 基于模式匹配操作符的查询,仅当模式存在一个常量,且该常量位于模式字符串的开头时

如col LIKE 'foo%'或col ~ '^foo',索引才会生效
否则将会执行全表扫描,如:col LIKE '%bar'
  • 默认情况下,B-tree索引将它的项以升序方式存储,并将空值放在最后

  • 在创建B-tree索引时,通过ASC、DESC、NULLS FIRST和NULLS LAST选项来改变索引的排序

CREATE INDEX test2_info_nulls_low ON test2 (info NULLS FIRST);
CREATE INDEX test3_desc_index ON test3 (id DESC NULLS LAST);

2、Hash索引

  • 散列(Hash)索引只能处理简单的等于比较

  • 当索引列使用等于操作符进行比较时,查询规划器会考虑使用散列索引

  • PostgreSQL散列索引的性能不比B-Tree索引强,但是散列索引的尺寸和构造时间则更差

  • 散列索引操作目前没有记录WAL日志,发生了DB崩溃,需用REINDEX重建散列索引

CREATE INDEX name ON table USING hash (column);


Hash索引已经存在了好多年。其思想是hash输入值并保存它,将来搜索的时候使用。
PostgreSQL 10之前,不建议使用,因为对它们,PostgreSQL没有WAL支持。PostgreSQL 10开始,Hash索引全部有WAL日志,可以复制(replication),并且是100% crash安全的。
一般来说,Hash索引比b-tree索引占空间。假设你要为400万个整数值建索引,btree索引大约需要90MB空间,而hash索引需要125MB空间。




3、GiST

  • GiST索引不是一种单独的索引类型,更像一种架构

  • GiST(通用搜索树)提供了一种用于存储数据的方式来构建平衡的树结构

  • 可以在该架构上实现很多不同的索引策略

  • 可以使GiST索引根据不同的索引策略,使用特定的操作符类型

  • 二维几何类型的以下操作符支持通过Gist索引访问


<< --严格在左侧,例如circle’((0,0),1)’ << circle’((5,0),1)’ &< --表示左边的平面体不会扩展到超过右边的平面体的右边. 例如box '((0,0),(1,1))' &< box '((0,0),(2,2))’ &>  --表示左边的平面体不会扩展到超过右边的平面体的左边. 例如box '((0,0),(3,3))' &> box '((0,0),(2,2))’
>> -- 严格在右
<<| --严格在下 &<| --不会扩展到超出上面 |&>  --不会扩展到超出下面
|>> -- 严格在上
@>  --包含
<@ --被包含 ~= --相同 && --相交




4、SP-GisST

  • SP-GisST与Gist类似,也是一种索引框架

  • 支持基于磁盘存储的非平衡数据结构如四叉树,k-d树,radix树

例如二维的point类型,Sp-Gist索引支持的操作符如下:
•      << • >>
•      ~=
•      <@ • <^ -- 在下面, circle '((0,0),1)' <^ circle '((0,5),1)’ • >^ -- 在上面, circle '((0,5),1)' >^ circle '((0,0),1)'


5、GIN

  • GIN索引是“倒排索引”,它可以处理包含多个键的值(比如数组)

  • 与GiST类似,GIN可以支持多种不同的用户定义的索引策略和特定操作符

  • 可以使GIN索引根据不同的索引策略,使用特定的操作符类型

  • 作为示例,PostgreSQL的标准发布中包含了用于一维数组的GIN操作符类型

如:<@、@>、=、&&等




6、多列索引

  • PostgreSQL中的索引可以定义在数据表的多个字段上

CREATE TABLE test2 (major int, minor int,name varchar);
CREATE INDEX test2_mm_idx ON test2 (major, minor);


  • 目前,只有B-tree、GiST、GIN 和 BRIN 支持多列索引,其中最多可以声明32个字段

  • 使用多列索引需要谨慎

• 在大多数情况下,单一字段上的索引就已经足够了,并且还节约时间和空间
• 除非表的使用模式非常固定,否则超过三个字段的索引几乎没什么用处


多列索引类型

使用说明

B-Tree

该索引字段的任意子集均可用于查询条件,但只有多列合索引中的第一个索引字段(最左边)被包含其中时,才可以获得最高效率。

GiST

只有当第一个索引字段被包含在查询条件中时,才能决定该查询会扫描多少索引数据,而其他索引字段上的条件只是会限制索引返回的条目。

假如第一个索引字段上的大多数数据都有相同的键值,那么此时应用GiST索引就会比较低效。

GIN

GIN复合索引不会受到查询条件中使用了哪些索引字段子集的影响,无论是哪种组合,都会得到相同的效率。

8、唯一索引

  • 只有B-Tree索引可以被声明为唯一索引

  • 如果索引声明为唯一索引,就不允许出现多个索引值相同的行(NULL值相互间不相等)

CREATE UNIQUE INDEX name ON table (column [, ...]);


9、表达式索引

主要用于在查询条件中存在基于某个字段的函数或表达式的结果与其他值进行比较时
例:SELECT * FROM test1 WHERE lower(col1) = 'value';
仅仅是在col1字段上建立索引,那么该查询在执行时会直接进行全表扫描
解决办法:CREATE INDEX test1_lower_col1_idx ON test1 (lower(col1));
如果我们把该索引声明为UNIQUE,那么它会禁止创建那种col1数值只是大小写有区别的数据行,以及col1数值完全相同的数据行。因此,在表达式上的索引可以用于强制那些无法定义为简单唯一约束的约束。
表达式索引对于检索速度远比插入和更新速度重要的情况非常有用!


10、函数索引

优化器需要知道给operator的参数值才能通过pg_statistic中统计到的表柱状图来计算走索引还是走全表扫描或者其他planner的开销最小, 如果传入的是个变量则通常不能使用索引扫描。
作为过滤条件的函数,immutable和stable的函数在优化器开始计算COST前会把函数值算出来。 而volatile的函数, 是在执行SQL的时候运行的, 所以无法在优化器计算执行计划的阶段得到函数值, 也就无法和pg_statistic中的信息比对到底是走索引呢还是全表扫描或其他执行计划。
  • Access Method(B-tree,Hash,GiST,SP-GiST,GIN)

  • 使用的operator class,以及opc中定义的operator或function

比如在一个SQL语句中,首先看operator是否在Access Method中支持,还要遵循CBO的选择
  • 还需要符合当前配置的Planner配置

enable_bitmapscan =on
enable_hahshjoin = on
enable_indexscan = on
如果都是off,都不会走索引扫描
  • 索引的成本跟哪些参数相关:

#random_page_cost = 4.0——随机页面扫描#cpu_index_tuple_cost = 0.005——索引扫描带来的索引的tuple的开销#effective_cache_size = 128MB——数值越大越倾向走索引扫描,因为数值越大数据可能都在OS cache里面,它的随机页面的成本就降低了

点击加载更多