一.唯一索引
唯一索引字面上理解就是在索引上增加唯一約束,不允許出現索引值相同的行,目前只有Btree索引可以聲明唯一索引,唯一鍵會自動創建唯一索引。
測試表:
test=# create table tbl_unique_index(a int, b int); CREATE TABLE
示例1.創建唯一索引,相等數據只允許插入一行,NULL除外,因為NULL不等於NULL。
test=# create unique index idx_unq_tbl_unique_index_a_b on tbl_unique_index using btree (a,b); CREATE INDEX test=# \d tbl_unique_index Table "public.tbl_unique_index" Column | Type | Modifiers --------+---------+----------- a | integer | b | integer | Indexes: "idx_unq_tbl_unique_index_a_b" UNIQUE, btree (a, b)
test=# insert into tbl_unique_index values (1,1); INSERT 0 1 test=# insert into tbl_unique_index values (1,1); ERROR: duplicate key value violates unique constraint "idx_unq_tbl_unique_index_a_b" DETAIL: Key (a, b)=(1, 1) already exists. test=# insert into tbl_unique_index values (1); INSERT 0 1 test=# insert into tbl_unique_index values (1); INSERT 0 1 test=# insert into tbl_unique_index values (1); INSERT 0 1
示例2.唯一鍵會自動創建唯一索引
test=# truncate table tbl_unique_index ; TRUNCATE TABLE test=# alter table tbl_unique_index add constraint pk_tbl_unique_index_a primary key(a); ALTER TABLE test=# alter table tbl_unique_index add constraint uk_tbl_unique_index_b unique(b); ALTER TABLE test=# \d tbl_unique_index Table "public.tbl_unique_index" Column | Type | Modifiers --------+---------+----------- a | integer | not null b | integer | Indexes: "pk_tbl_unique_index_a" PRIMARY KEY, btree (a) "idx_unq_tbl_unique_index_a_b" UNIQUE, btree (a, b) "uk_tbl_unique_index_b" UNIQUE CONSTRAINT, btree (b)
二.表達式索引
除針對表的字段直接創建索引外,還可以對字段進行某種運算之后的結果創建索引。
測試表
test=# create table tbl_expression(a varchar(32), b varchar(32)); CREATE TABLE
test=# insert into tbl_expression select concat('test',x),concat('you',x) from generate_series(1,10000) x; INSERT 0 10000
如果此時分別在a和b字段上各創建一個Btree索引,分別使用a和b字段查詢時會進行索引掃描。
test=# create index idx_tbl_expression_a on tbl_expression using btree (a); CREATE INDEX test=# create index idx_tbl_expression_b on tbl_expression using btree (b); CREATE INDEX test=# test=# explain analyze select * from tbl_expression where a = 'TEST'; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------- - Index Scan using idx_tbl_expression_a on tbl_expression (cost=0.29..8.30 rows=1 width=15) (actual time=0.130..0.130 rows=0 loops=1) Index Cond: ((a)::text = 'TEST'::text) Planning time: 0.667 ms Execution time: 0.168 ms (4 rows) test=# explain analyze select * from tbl_expression where b = 'you'; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------- - Index Scan using idx_tbl_expression_b on tbl_expression (cost=0.29..8.30 rows=1 width=15) (actual time=0.171..0.171 rows=0 loops=1) Index Cond: ((b)::text = 'you'::text) Planning time: 0.126 ms Execution time: 0.206 ms (4 rows)
但是下面的兩種查詢方式是不會進行索引掃描的
select * from tbl_expression where upper(a) = 'TEST'; select * from tbl_expression where (a || ' ' ||b) = 'test you';
test=# explain analyze select * from tbl_expression where upper(a) = 'TEST'; QUERY PLAN ------------------------------------------------------------------------------------------------------------ Seq Scan on tbl_expression (cost=0.00..166.00 rows=50 width=15) (actual time=5.957..5.957 rows=0 loops=1) Filter: (upper((a)::text) = 'TEST'::text) Rows Removed by Filter: 10000 Planning time: 0.140 ms Execution time: 6.014 ms (5 rows) test=# test=# explain analyze select * from tbl_expression where (a || ' ' ||b) = 'test you'; QUERY PLAN ------------------------------------------------------------------------------------------------------------ Seq Scan on tbl_expression (cost=0.00..191.00 rows=50 width=15) (actual time=7.851..7.851 rows=0 loops=1) Filter: ((((a)::text || ' '::text) || (b)::text) = 'test you'::text) Rows Removed by Filter: 10000 Planning time: 0.114 ms Execution time: 7.883 ms (5 rows)
此時就可以使用表達式創建索引來解決此類全表掃描問題。
test=# explain analyze select * from tbl_expression where upper(a) = 'TEST'; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------ Bitmap Heap Scan on tbl_expression (cost=4.67..21.42 rows=50 width=15) (actual time=0.133..0.133 rows=0 loops=1) Recheck Cond: (upper((a)::text) = 'TEST'::text) -> Bitmap Index Scan on idx_tbl_expression_upper_a (cost=0.00..4.66 rows=50 width=0) (actual time=0.129..0.129 rows=0 loops=1) Index Cond: (upper((a)::text) = 'TEST'::text) Planning time: 0.565 ms Execution time: 0.175 ms (6 rows)
test=# create index idx_tbl_expression_a_b on tbl_expression ((a||' '||b)); CREATE INDEX test=# explain analyze select * from tbl_expression where (a || ' ' ||b) = 'test you'; QUERY PLAN -------------------------------------------------------------------------------------------------------------------------------- Bitmap Heap Scan on tbl_expression (cost=4.67..21.55 rows=50 width=15) (actual time=0.130..0.130 rows=0 loops=1) Recheck Cond: ((((a)::text || ' '::text) || (b)::text) = 'test you'::text) -> Bitmap Index Scan on idx_tbl_expression_a_b (cost=0.00..4.66 rows=50 width=0) (actual time=0.128..0.128 rows=0 loops=1) Index Cond: ((((a)::text || ' '::text) || (b)::text) = 'test you'::text) Planning time: 0.582 ms Execution time: 0.187 ms (6 rows)
但是還是需要根據實際業務情況仔細評估后決定采用何種索引,因為並不是索引越多越好。
三.部分索引
只在自己感興趣的那部分數據上創建索引,而不是對每一行數據都創建索引,此種方式創建索引就需要使用WHERE條件了。
創建兩個完全相同的表比較部分索引和全索引的區別。
測試表
test=# create table tbl_partial_index(id bigint,alarm_time timestamp without time zone,level varchar(12),alarm_desc varchar(100)); CREATE TABLE test=# create table tbl_partial_index1(id bigint,alarm_time timestamp without time zone,level varchar(12),alarm_desc varchar(100)); CREATE TABLE
寫入完全相同的數據
test=# insert into tbl_partial_index(id,alarm_time,level,alarm_desc)
select generate_series(1,9000000),clock_timestamp()::timestamp without time zone,'green','正常'; INSERT 0 9000000 test=# insert into tbl_partial_index(id,alarm_time,level,alarm_desc)
select generate_series(9000000,9000100),clock_timestamp()::timestamp without time zone,'red','攻擊'; INSERT 0 101 test=# test=# test=# insert into tbl_partial_index1(id,alarm_time,level,alarm_desc)
select generate_series(1,9000000),clock_timestamp()::timestamp without time zone,'green','正常'; INSERT 0 9000000 test=# insert into tbl_partial_index1(id,alarm_time,level,alarm_desc)
select generate_series(9000000,9000100),clock_timestamp()::timestamp without time zone,'red','攻擊'; INSERT 0 101
示例1.在tbl_partial_index表字段level上創建索引
test=# create index idx_tbl_partial_index_level on tbl_partial_index using btree (level); CREATE INDEX Time: 31407.356 ms test=# test=# explain analyze select * from tbl_partial_index where level = 'red'; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------- ------------- Index Scan using idx_tbl_partial_index_level on tbl_partial_index (cost=0.43..4.45 rows=1 width=29) (actual time=0.069..0.087 rows= 101 loops=1) Index Cond: ((level)::text = 'red'::text) Planning time: 0.268 ms Execution time: 0.124 ms (4 rows) Time: 23.460 ms
test=# select relname,pg_size_pretty(pg_relation_size(oid)) from pg_class where relname='idx_tbl_partial_index_level'; relname | pg_size_pretty -----------------------------+---------------- idx_tbl_partial_index_level | 191 MB (1 row) Time: 71.799 ms
示例2.在tbl_partial_index1表字段level等於red的行上創建索引
test=# create index idx_tbl_partial_index1_level on tbl_partial_index1(level) where level = 'red'; CREATE INDEX Time: 5558.905 ms test=# explain analyze select * from tbl_partial_index1 where level = 'red'; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------- --------------- Index Scan using idx_tbl_partial_index1_level on tbl_partial_index1 (cost=0.14..4.16 rows=1 width=29) (actual time=0.051..0.082 row s=101 loops=1) Planning time: 18.922 ms Execution time: 0.136 ms (3 rows) Time: 19.929 ms
test=# select relname,pg_size_pretty(pg_relation_size(oid)) from pg_class where relname='idx_tbl_partial_index1_level'; relname | pg_size_pretty ------------------------------+---------------- idx_tbl_partial_index1_level | 64 kB (1 row) Time: 0.950 ms
比較上面兩個示例的結果可知,全表索引在耗時和大小方面要比部分索引消耗更多的資源,查詢'red'的數據排除環境影響基本相同,數據量更大,'red'占比更小時性能可能會有明顯差異,但是查詢非'red'數據時全表索引會有明顯的性能優勢,因為部分索引並沒有'green'數據的索引,走的是全表掃描。
綜上,根據數據的使用方式創建不同的索引在性能上是有明顯差異的。