索引主要被用来提升数据库性能,不当的使用会导致性能变差。 PostgreSQL 提供了多种索引类型: B-tree、Hash、GiST、SP-GiST 、GIN 和 BRIN。每一种索引类型使用了一种不同的算法来适应不同类型的查询。默认情况下,CREATE INDEX 命令创建适合于大部分情况的 B-tree 索引。
在使用上,除了常见的单列索引,还有多列索引、唯一索引、表达式索引、部分索引、覆盖索引等。
CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] name ] ON [ ONLY ] table_name [ USING method ]
( { column_name | ( expression ) } [ COLLATE collation ] [ opclass [ ( opclass_parameter = value [, ... ] ) ] ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
[ INCLUDE ( column_name [, ...] ) ]
[ WITH ( storage_parameter [= value] [, ... ] ) ]
[ TABLESPACE tablespace_name ]
[ WHERE predicate ]
UNIQUE:唯一索引,在索引被创建时(如果数据已经存在)或者加入数据时检查重复值。
CONCURRENTLY:在构建索引时不会取得任何会阻止该表上并发插入、更新或者删除的锁。而标准的索引构建将会把表锁住以阻止对表的写(但不阻塞读),这种锁定会持续到索引创建完毕。
IF NOT EXISTS:如果一个同名关系已经存在则不要抛出错误。
INCLUDE:指定一个列的列表,其中的列将被包括在索引中作为非键列。不能作为索引扫描的条件,主要作用是相关数据索存储在索引中,访问时无需访问该索引的基表。当前,有B-树和GiST索引访问方法支持这一特性。
name:要创建的索引名称。这里不能包括模式名,因为索引总是被创建在其基表所在的模式中。如果索引名称被省略,PostgreSQL 将基于基表名称和被索引列名称选择一个合适的名称。
ONLY:如果该表是分区表,指示不要在分区上递归创建索引。默认会递归创建索引。
table_name:要被索引的表的名称(可以被模式限定)。
method:要使用的索引方法的名称。可以选择 btree、hash、 gist、spgist、 gin 以及brin。默认方法是 btree。
column_name:一个表列的名称。
expression:一个基于一个或者更多个表列的表达式。如语法中所示,表达式通常必须被写在圆括号中。不过,如果该表达式是一个函数调用的形式,圆括号可以被省略。
collation:要用于该索引的排序规则的名称。
opclass:一个操作符类的名称。
opclass_parameter:运算符类参数的名称。
ASC:指定上升排序(默认)。
DESC:指定下降排序。
NULLS FIRST:指定把空值排序在非空值前面。在指定DESC时,这是默认行为。
NULLS LAST:指定把空值排序在非空值后面。在没有指定DESC时,这是默认行为。
storage_parameter:索引方法相关的存储参数的名称。可选的WITH子句为索引指定存储参数。每一种 索引方法都有自己的存储参数集合。B-树、哈希、GiST以及SP-GiST索引方法都接受这个参数:fillfactor (integer) :索引的填充因子是一个百分数,它决定索引方法将尝试填充索引页面的充满程度。对于B-树,在初始的索引构建过程中,叶子页面会被填充至该百分数,当在索引右端扩展索引(增加新的最大键值)时也会这样处理。如果页面后来被完全填满,它们就会被分裂,导致索引的效率逐渐退化。B-树使用了默认的填充因子 90,但是也可以选择为 10 到 100 的任何整数值。如果表是静态的,那么填充因子 100 是最好的,因为它可以让索引的物理尺寸最小化。但是对于更新负荷很重的表,较小的填充因子有利于最小化对页面分裂的需求。其他索引方法以不同但是大致类似的方式使用填充因子,不同方法的默认填充因子也不相同。
deduplicate_items (boolean):B 树重复数据删除技术的使用。设置为 ON 或 OFF 以启用或禁用优化。默认值为ON。
vacuum_cleanup_index_scale_factor:指定在以前的统计信息收集过程中计数到的堆元组总数的一个分数,插入不超过这一数量所代表的元组不会导致VACUUM清理阶段的索引扫描。这个设置当前仅适用于B-树索引。
buffering (enum):适用于 GiST 索引,决定是否用缓冲构建技术来构建索引。OFF 会禁用它,ON 则启用该特性,如果设置为 AUTO 则初始会禁用它,但是一旦索引尺寸到达 effective_cache_size 就会随时打开。默认值是 AUTO。
fastupdate (boolean):适用于 GIN 索引,这个设置控制快速更新技术的使用。它是一个布尔参数:ON 启用快速更新,OFF 禁用。默认是 ON。
gin_pending_list_limit (integer):适用于 GIN 索引,设置 fastupdate 被启用时可以使用的 GIN 索引的待处理列表的最大尺寸。 如果该列表增长到超过这个最大尺寸,会通过批量将其中的项移入索引的主 GIN 数据结构来清理列表。 如果指定值时没有单位,则以千字节为单位。默认值是四兆字节(4MB)。可以通过更改索引的存储参数来为个别 GIN 索引覆盖这个设置。
pages_per_range (integer):使用于 BRIN 索引,定义用于每一个BRIN索引项的块范围由多少个表块组成。默认是128。
autosummarize (boolean):定义是否只要在下一个页面上检测到插入就为前面的页面范围运行概要操作。
postgres=# create index idx_test_id on test(id);
CREATE INDEX
postgres=# \d test
Table "public.test"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------
id | integer | | |
name | text | | |
Indexes:
"idx_test_id" btree (id)
postgres=# explain analyze select * from test where id between 100 and 200;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
Index Scan using idx_test_id on test (cost=0.43..10.49 rows=103 width=15) (actual time=0.006..0.058 rows=101 loops=1)
Index Cond: ((id >= 100) AND (id <= 200))
Planning Time: 0.408 ms
Execution Time: 0.072 ms
(4 rows)
postgres=# create index idx_test_id on test using hash(id);
CREATE INDEX
postgres=# \d test
Table "public.test"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------
id | integer | | |
name | text | | |
Indexes:
"idx_test_id" hash (id)
postgres=# explain analyze select * from test where id = 100;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------
Index Scan using idx_test_id on test (cost=0.00..8.02 rows=1 width=15) (actual time=0.014..0.015 rows=1 loops=1)
Index Cond: (id = 100)
Planning Time: 0.142 ms
Execution Time: 0.029 ms
(4 rows)
postgres=# create index idx_t_gist_pos on t_gist using gist(pos);
CREATE INDEX
postgres=# \d t_gist
Table "public.t_gist"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------
id | integer | | |
pos | point | | |
Indexes:
"idx_t_gist_pos" gist (pos)
postgres=# explain analyze select * from t_gist where circle '((100,100) 10)' @> pos;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on t_gist (cost=5.06..271.70 rows=100 width=20) (actual time=0.048..0.092 rows=29 loops=1)
Recheck Cond: ('<(100,100),10>'::circle @> pos)
Heap Blocks: exact=29
-> Bitmap Index Scan on idx_t_gist_pos (cost=0.00..5.03 rows=100 width=0) (actual time=0.027..0.027 rows=29 loops=1)
Index Cond: (pos <@ '<(100,100),10>'::circle)
Planning Time: 0.092 ms
Execution Time: 0.136 ms
(7 rows)
postgres=# create index idx_t_spgist_p on t_spgist using spgist(p);
CREATE INDEX
postgres=# \d t_spgist
Table "public.t_spgist"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------
id | integer | | |
p | point | | |
Indexes:
"idx_t_spgist_p" spgist (p)
postgres=# explain analyze select * from t_spgist where p >^ point '(2,7)';
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on t_spgist (cost=305.78..1067.78 rows=10000 width=20) (actual time=7.387..15.011 rows=99245 loops=1)
Recheck Cond: (p >^ '(2,7)'::point)
Heap Blocks: exact=637
-> Bitmap Index Scan on idx_t_spgist_p (cost=0.00..303.28 rows=10000 width=0) (actual time=7.302..7.303 rows=99245 loops=1)
Index Cond: (p >^ '(2,7)'::point)
Planning Time: 0.124 ms
Execution Time: 17.611 ms
(7 rows)
postgres=# create index idx_ts_doc_tsv on ts using gin(doc_tsv);
CREATE INDEX
postgres=# \d ts
Table "public.ts"
Column | Type | Collation | Nullable | Default
---------+----------+-----------+----------+---------
doc | text | | |
doc_tsv | tsvector | | |
Indexes:
"idx_ts_doc_tsv" gin (doc_tsv)
postgres=# set enable_seqscan = off;
SET
postgres=# explain analyze select * from ts where doc_tsv @@ to_tsquery('many & slitter');
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on ts (cost=12.25..16.51 rows=1 width=64) (actual time=0.046..0.047 rows=1 loops=1)
Recheck Cond: (doc_tsv @@ to_tsquery('many & slitter'::text))
Heap Blocks: exact=1
-> Bitmap Index Scan on idx_ts_doc_tsv (cost=0.00..12.25 rows=1 width=0) (actual time=0.042..0.042 rows=1 loops=1)
Index Cond: (doc_tsv @@ to_tsquery('many & slitter'::text))
Planning Time: 0.176 ms
Execution Time: 0.071 ms
(7 rows)
postgres=# create index idx_test_id on test using brin(id);
CREATE INDEX
postgres=# \d test
Table "public.test"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------
id | integer | | |
name | text | | |
Indexes:
"idx_test_id" brin (id)
postgres=# explain analyze select * from test where id between 100 and 200;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on test (cost=12.03..27651.36 rows=1 width=15) (actual time=0.079..4.006 rows=101 loops=1)
Recheck Cond: ((id >= 100) AND (id <= 200))
Rows Removed by Index Recheck: 23579
Heap Blocks: lossy=128
-> Bitmap Index Scan on idx_test_id (cost=0.00..12.03 rows=23585 width=0) (actual time=0.067..0.067 rows=1280 loops=1)
Index Cond: ((id >= 100) AND (id <= 200))
Planning Time: 0.240 ms
Execution Time: 4.045 ms
(8 rows)
--多列索引
postgres=# create index idx_test_dl on test(id,name);
CREATE INDEX
postgres=# \d test
Table "public.test"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------
id | integer | | |
name | text | | |
Indexes:
"idx_test_dl" btree (id, name)
postgres=# explain analyze select * from test where id = 100 and name = 'val:100';
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
Index Only Scan using idx_test_dl on test (cost=0.43..8.45 rows=1 width=15) (actual time=0.086..0.087 rows=1 loops=1)
Index Cond: ((id = 100) AND (name = 'val:100'::text))
Heap Fetches: 1
Planning Time: 0.224 ms
Execution Time: 0.104 ms
(5 rows)
--唯一索引
postgres=# create unique index idx_test_wy on test(id);
CREATE INDEX
postgres=# \d test
Table "public.test"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------
id | integer | | |
name | text | | |
Indexes:
"idx_test_wy" UNIQUE, btree (id)
postgres=# explain analyze select * from test where id = 100;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------
Index Scan using idx_test_wy on test (cost=0.43..8.45 rows=1 width=15) (actual time=0.099..0.101 rows=1 loops=1)
Index Cond: (id = 100)
Planning Time: 0.350 ms
Execution Time: 0.119 ms
(4 rows)
--表达式索引
postgres=# create index idx_test_bds on test((id+1000));
CREATE INDEX
postgres=# \d test
Table "public.test"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------
id | integer | | |
name | text | | |
Indexes:
"idx_test_bds" btree ((id + 1000))
postgres=# explain analyze select * from test where id+1000 > 10000;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on test (cost=31205.10..83233.11 rows=1666667 width=15) (actual time=578.175..1501.361 rows=4991000 loops=1)
Recheck Cond: ((id + 1000) > 10000)
Heap Blocks: exact=26980
-> Bitmap Index Scan on idx_test_bds (cost=0.00..30788.43 rows=1666667 width=0) (actual time=574.862..574.863 rows=4991000 loops=1)
Index Cond: ((id + 1000) > 10000)
Planning Time: 0.110 ms
Execution Time: 1633.012 ms
(7 rows)
--部分索引
postgres=# create index idx_test_bf on test(id) where (id between 1000 and 2000);
CREATE INDEX
postgres=# \d test
Table "public.test"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------
id | integer | | |
name | text | | |
Indexes:
"idx_test_bf" btree (id) WHERE id >= 1000 AND id <= 2000
postgres=# explain analyze select * from test where id between 1500 and 2000;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
Index Scan using idx_test_bf on test (cost=0.28..27.29 rows=515 width=15) (actual time=0.035..0.090 rows=501 loops=1)
Index Cond: (id >= 1500)
Planning Time: 0.265 ms
Execution Time: 0.110 ms
(4 rows)
postgres=# explain analyze select * from test where id between 500 and 1000;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------
Seq Scan on test (cost=10000000000.00..10000102028.00 rows=272 width=15) (actual time=0.035..427.097 rows=501 loops=1)
Filter: ((id >= 500) AND (id <= 1000))
Rows Removed by Filter: 4999499
Planning Time: 0.107 ms
Execution Time: 427.120 ms
(5 rows)
--覆盖索引
postgres=# create index idx_test_fg on test(id) include(name);
CREATE INDEX
postgres=# \d test
Table "public.test"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------
id | integer | | |
name | text | | |
Indexes:
"idx_test_fg" btree (id) INCLUDE (name)
postgres=# explain analyze select * from test where id = 100;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
Index Only Scan using idx_test_fg on test (cost=0.43..8.45 rows=1 width=15) (actual time=0.031..0.032 rows=1 loops=1)
Index Cond: (id = 100)
Heap Fetches: 1
Planning Time: 0.264 ms
Execution Time: 0.047 ms
(5 rows)
--查询
postgres=# select * from pg_indexes where tablename = 'test';
schemaname | tablename | indexname | tablespace | indexdef
------------+-----------+-------------+------------+-------------------------------------------------------------------------
public | test | idx_test_fg | | CREATE INDEX idx_test_fg ON public.test USING btree (id) INCLUDE (name)
(1 row)
--重建
postgres=# reindex index idx_test_fg;
REINDEX
--重命名
postgres=# alter index idx_test_fg rename to idx_test_id;
ALTER INDEX
--修改表空间
postgres=# alter index idx_test_id set tablespace tab1;
ALTER INDEX
--删除
postgres=# drop index idx_test_id;
DROP INDEX