阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

浅谈PostgreSQL的索引

447次阅读
没有评论

共计 8179 个字符,预计需要花费 21 分钟才能阅读完成。

1. 索引的特性

1.1 加快条件的检索的特性

当表数据量越来越大时查询速度会下降,在表的条件字段上使用索引,快速定位到可能满足条件的记录,不需要遍历所有记录。

create table t(id int, info text);
insert into t select generate_series(1,10000),'lottu'||generate_series(1,10000);
create table t1 as select * from t;
create table t2 as select * from t;
create index ind_t2_id on t2(id);
 
lottu=# analyze t1;
ANALYZE
lottu=# analyze t2;
ANALYZE
# 没有索引
lottu=# explain (analyze,buffers,verbose) select * from t1 where id < 10;
                                             QUERY PLAN                                              
-----------------------------------------------------------------------------------------------------
 Seq Scan on lottu.t1  (cost=0.00..180.00 rows=9 width=13) (actual time=0.073..5.650 rows=9 loops=1)
   Output: id, info
   Filter: (t1.id < 10)
   Rows Removed by Filter: 9991
   Buffers: shared hit=55
 Planning time: 25.904 ms
 Execution time: 5.741 ms
(7 rows)
# 有索引
lottu=# explain (analyze,verbose,buffers) select * from t2 where id < 10;
                                                     QUERY PLAN                                                      
---------------------------------------------------------------------------------------------------------------------
 Index Scan using ind_t2_id on lottu.t2  (cost=0.29..8.44 rows=9 width=13) (actual time=0.008..0.014 rows=9 loops=1)
   Output: id, info
   Index Cond: (t2.id < 10)
   Buffers: shared hit=3
 Planning time: 0.400 ms
 Execution time: 0.052 ms
(6 rows)

# 在这个案例中:执行同一条 SQL。t2 有索引的执行数据是 0.052 ms;t1 没有索引的是:5.741 ms; 

1.2 有序的特性

索引本身就是有序的。

# 没有索引
lottu=# explain (analyze,verbose,buffers) select * from t1 where id > 2 order by id;
                                                   QUERY PLAN                                                    
-----------------------------------------------------------------------------------------------------------------
Sort  (cost=844.31..869.31 rows=9999 width=13) (actual time=8.737..11.995 rows=9998 loops=1)
   Output: id, info
   Sort Key: t1.id
   Sort Method: quicksort  Memory: 853kB
   Buffers: shared hit=55
   ->  Seq Scan on lottu.t1  (cost=0.00..180.00 rows=9999 width=13) (actual time=0.038..5.133 rows=9998 loops=1)
         Output: id, info
         Filter: (t1.id > 2)
         Rows Removed by Filter: 2
         Buffers: shared hit=55
 Planning time: 0.116 ms
 Execution time: 15.205 ms
(12 rows)
 #有索引
lottu=# explain (analyze,verbose,buffers) select * from t2 where id > 2 order by id;
                                                         QUERY PLAN                                                          
-----------------------------------------------------------------------------------------------------------------------------
 Index Scan using ind_t2_id on lottu.t2  (cost=0.29..353.27 rows=9999 width=13) (actual time=0.030..5.304 rows=9998 loops=1)
   Output: id, info
   Index Cond: (t2.id > 2)
   Buffers: shared hit=84
 Planning time: 0.295 ms
 Execution time: 7.027 ms
(6 rows)

# 在这个案例中:执行同一条 SQL。

  • t2 有索引的执行数据是 7.027 ms;t1 没有索引的是:15.205 ms;
  • t1 没有索引执行还占用了 Memory: 853kB。

2. 索引扫描方式

索引的扫描方式有 3 种

2.1 Indexscan

先查索引找到匹配记录的 ctid,再通过 ctid 查堆表

2.2 bitmapscan

先查索引找到匹配记录的 ctid 集合,把 ctid 通过 bitmap 做集合运算和排序后再查堆表

2.3 Indexonlyscan

如果索引字段中包含了所有返回字段,对可见性映射 (vm)中全为可见的数据块,不查堆表直接返回索引中的值。

这里谈谈 Indexscan 扫描方式和 Indexonlyscan 扫描方式
对这两种扫描方式区别;借用 Oracle 中索引扫描方式来讲;Indexscan 扫描方式会产生回表读。根据上面解释来说;Indexscan 扫描方式:查完索引之后还需要查表。Indexonlyscan 扫描方式只需要查索引。也就是说:Indexonlyscan 扫描方式要优于 Indexscan 扫描方式?我们来看看

现有表 t;在字段 id 上面建来 ind_t_id 索引
1. t 表没有 VM 文件。lottu=# \d+ t
                           Table "lottu.t"
 Column |  Type   | Modifiers | Storage  | Stats target | Description 
--------+---------+-----------+----------+--------------+-------------
 id     | integer |           | plain    |              | 
 info   | text    |           | extended |              | 
Indexes:
    "ind_t_id" btree (id)

lottu=# explain (analyze,buffers,verbose) select id from t where id < 10;
                                                      QUERY PLAN                                                       
-----------------------------------------------------------------------------------------------------------------------
 Index Only Scan using ind_t_id on lottu.t  (cost=0.29..8.44 rows=9 width=4) (actual time=0.009..0.015 rows=9 loops=1)
   Output: id
   Index Cond: (t.id < 10)
   Heap Fetches: 9
   Buffers: shared hit=3
 Planning time: 0.177 ms
 Execution time: 0.050 ms
(7 rows)
#人为更改执行计划
lottu=# set enable_indexonlyscan = off;
SET
lottu=# explain (analyze,buffers,verbose) select id from t where id < 10;
                                                    QUERY PLAN                                                    
------------------------------------------------------------------------------------------------------------------
 Index Scan using ind_t_id on lottu.t  (cost=0.29..8.44 rows=9 width=4) (actual time=0.008..0.014 rows=9 loops=1)
   Output: id
   Index Cond: (t.id < 10)
   Buffers: shared hit=3
 Planning time: 0.188 ms
 Execution time: 0.050 ms
(6 rows)
# 可以发现两者几乎没有差异;唯一不同的是 Indexonlyscan 扫描方式存在扫描的 Heap Fetches 时间。这个时间是不在 Execution time 里面的。2. t 表有 VM 文件
lottu=# delete from t where id >200 and id < 500;
DELETE 299
lottu=# vacuum t;
VACUUM
lottu=# analyze t;
ANALYZE
lottu=# explain (analyze,buffers,verbose) select id from t where id < 10;
                                                      QUERY PLAN                                                       
-----------------------------------------------------------------------------------------------------------------------
 Index Only Scan using ind_t_id on lottu.t  (cost=0.29..4.44 rows=9 width=4) (actual time=0.008..0.012 rows=9 loops=1)
   Output: id
   Index Cond: (t.id < 10)
   Heap Fetches: 0
   Buffers: shared hit=3
 Planning time: 0.174 ms
 Execution time: 0.048 ms
(7 rows)

lottu=# set enable_indexonlyscan = off;
SET
lottu=# explain (analyze,buffers,verbose) select id from t where id < 10;
                                                    QUERY PLAN                                                    
------------------------------------------------------------------------------------------------------------------
 Index Scan using ind_t_id on lottu.t  (cost=0.29..8.44 rows=9 width=4) (actual time=0.012..0.022 rows=9 loops=1)
   Output: id
   Index Cond: (t.id < 10)
   Buffers: shared hit=3
 Planning time: 0.179 ms
 Execution time: 0.077 ms
(6 rows)

总结:

  • Index Only Scan 在没有 VM 文件的情况下, 速度比 Index Scan 要慢, 因为要扫描所有的 Heap page。差异几乎不大。
  • Index Only Scan 存在 VM 文件的情况下,是���比 Index Scan 要快。

知识点 1:

  • VM 文件:称为可见性映射文件;该文件存在表示:该数据块没有需要清理的行。即已经做了 vaccum 操作。

知识点 2:

人为选择执行计划。可设置 enable_xxx 参数有

  • enable_bitmapscan
  • enable_hashagg
  • enable_hashjoin
  • enable_indexonlyscan
  • enable_indexscan
  • enable_material
  • enable_mergejoin
  • enable_nestloop
  • enable_seqscan
  • enable_sort
  • enable_tidscan

参考文献

  • 参考德哥:《PostgreSQL 性能优化培训 3 DAY.pdf》
  • https://www.postgresql.org/docs/9.6/static/runtime-config-query.html

3. 索引的类型

PostgreSQL 支持索引类型有: B-tree, Hash, GiST, SP-GiST, GIN and BRIN。

  • postgresql—-Btree 索引:http://www.cnblogs.com/alianbog/p/5621749.html
  • postgresql—-hash 索引:一般只用于简单等值查询。不常用。
  • postgresql—-Gist 索引:http://www.cnblogs.com/alianbog/p/5628543.html

4. 索引的管理

4.1 创建索引

创建索引语法:

lottu=# \h create index
Command:     CREATE INDEX
Description: define a new index
Syntax:
CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [IF NOT EXISTS ] name ] ON table_name [ USING method ]
    ({ column_name | (expression) } [ COLLATE collation ] [ opclass ] [ ASC | DESC ] [ NULLS {FIRST | LAST} ] [, ...] )
    [ WITH (storage_parameter = value [, ... ] ) ]
    [ TABLESPACE tablespace_name ]
    [ WHERE predicate ]
接下来我们以 t 表为例。1. 关键字【UNIQUE】#创建唯一索引;主键就是一种唯一索引
CREATE UNIQUE INDEX ind_t_id_1 on t (id);
2. 关键字【CONCURRENTLY】# 这是并发创建索引。跟 oracle 的 online 创建索引作用是一样的。创建索引过程中;不会阻塞表更新,插入,删除操作。当然创建的时间就会很漫长。CREATE INDEX CONCURRENTLY ind_t_id_2 on t (id);
3. 关键字【IF NOT EXISTS】#用该命令是用于确认索引名是否存在。若存在;也不会报错。CREATE INDEX IF NOT EXISTS ind_t_id_3 on t (id);
4. 关键字【USING】# 创建哪种类型的索引。默认是 B -tree。CREATE INDEX ind_t_id_4 on t using btree (id);
5 关键字【[ ASC | DESC ] [ NULLS {FIRST | LAST]】# 创建索引是采用降序还是升序。若字段存在 null 值,是把 null 值放在前面还是最后:例如采用降序,null 放在前面。CREATE INDEX ind_t_id_5 on t (id desc nulls first)
6. 关键字【WITH (storage_parameter = value)】#索引的填充因子设为。例如创建索引的填充因子设为 75
CREATE INDEX ind_t_id_6 on t (id) with (fillfactor = 75);
7. 关键字【TABLESPACE】#是把索引创建在哪个表空间。CREATE INDEX ind_t_id_7 on t (id) TABLESPACE tsp_lottu;
8. 关键字【WHERE】#只在自己感兴趣的那部分数据上创建索引,而不是对每一行数据都创建索引,此种方式创建索引就需要使用 WHERE 条件了。CREATE INDEX ind_t_id_8 on t (id) WHERE id < 1000;

4.2 修改索引

修改索引语法

lottu=# \h alter index
Command:     ALTER INDEX
Description: change the definition of an index
Syntax:
#把索引重新命名
ALTER INDEX [ IF EXISTS ] name RENAME TO new_name
#把索引迁移表空间
ALTER INDEX [ IF EXISTS ] name SET TABLESPACE tablespace_name
#把索引重设置填充因子
ALTER INDEX [ IF EXISTS ] name SET (storage_parameter = value [, ... ] )
#把索引的填充因子设置为默认值
ALTER INDEX [ IF EXISTS ] name RESET (storage_parameter [, ... ] )
#把表空间 TSP1 中索引迁移到新表空间
ALTER INDEX ALL IN TABLESPACE name [ OWNED BY role_name [, ... ] ]
    SET TABLESPACE new_tablespace [ NOWAIT ]

4.3 删除索引

删除索引语法

lottu=# \h drop index
Command:     DROP INDEX
Description: remove an index
Syntax:
DROP INDEX [ CONCURRENTLY ] [ IF EXISTS ] name [, ...] [ CASCADE | RESTRICT ]

5. 索引的维护

索引能带来加快对表中记录的查询,排序,以及唯一约束的作用。索引也是有代价

  • 索引需要增加数据库的存储空间。
  • 在表记录执行插入,更新,删除操作。索引也要更新。

5.1 查看索引的大小

select pg_size_pretty(pg_relation_size('ind_t_id'));

5.2 索引的利用率

--通过 pg_stat_user_indexes.idx_scan 可检查利用索引进行扫描的次数;这样可以确认那些索引可以清理掉。
select idx_scan from pg_stat_user_indexes where indexrelname = 'ind_t_id';

5.3 索引的重建

--如果一个表经过频繁更新之后,索引性能不好;需要重建索引。
lottu=# select pg_size_pretty(pg_relation_size('ind_t_id_1')); 
 pg_size_pretty 
----------------
 2200 kB
(1 row)

lottu=# delete from t where id > 1000;
DELETE 99000

lottu=# analyze t;
ANALYZE
lottu=# select pg_size_pretty(pg_relation_size('ind_t_id_1')); 
 pg_size_pretty 
----------------
 2200 kB
 
lottu=# insert into t select generate_series(2000,100000),'lottu';
INSERT 0 98001

lottu=# select pg_size_pretty(pg_relation_size('ind_t_id_1')); 
 pg_size_pretty 
----------------
 4336 kB
(1 row)

lottu=# vacuum full t;
VACUUM

lottu=# select pg_size_pretty(pg_relation_size('ind_t_id_1')); 
 pg_size_pretty 
----------------
 2176 kB
 
重建方法:1. reindex:reindex 不支持并行重建【CONCURRENTLY】; 索引会锁表;会进行阻塞。2. vacuum full; 对表进行重构;索引也会重建;同样也会锁表。3. 创建一个新索引(索引名不同);再删除旧索引。

本文永久更新链接地址:http://www.linuxidc.com/Linux/2017-09/146895.htm

正文完
星哥玩云-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2022-01-22发表,共计8179字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中

星哥玩云

星哥玩云
星哥玩云
分享互联网知识
用户数
4
文章数
19351
评论数
4
阅读量
7974965
文章搜索
热门文章
星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛 NAS-6:抖音视频同步工具,视频下载自动下载保存 前言 各位玩 NAS 的朋友好,我是星哥!...
星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛 NAS-3:安装飞牛 NAS 后的很有必要的操作 前言 如果你已经有了飞牛 NAS 系统,之前...
我把用了20年的360安全卫士卸载了

我把用了20年的360安全卫士卸载了

我把用了 20 年的 360 安全卫士卸载了 是的,正如标题你看到的。 原因 偷摸安装自家的软件 莫名其妙安装...
再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见 zabbix!轻量级自建服务器监控神器在 Linux 的完整部署指南 在日常运维中,服务器监控是绕不开的...
飞牛NAS中安装Navidrome音乐文件中文标签乱码问题解决、安装FntermX终端

飞牛NAS中安装Navidrome音乐文件中文标签乱码问题解决、安装FntermX终端

飞牛 NAS 中安装 Navidrome 音乐文件中文标签乱码问题解决、安装 FntermX 终端 问题背景 ...
阿里云CDN
阿里云CDN-提高用户访问的响应速度和成功率
随机文章
星哥带你玩飞牛NAS硬件02:某鱼6张左右就可拿下5盘位的飞牛圣体NAS

星哥带你玩飞牛NAS硬件02:某鱼6张左右就可拿下5盘位的飞牛圣体NAS

星哥带你玩飞牛 NAS 硬件 02:某鱼 6 张左右就可拿下 5 盘位的飞牛圣体 NAS 前言 大家好,我是星...
【1024程序员】我劝你赶紧去免费领一个AWS、华为云等的主机

【1024程序员】我劝你赶紧去免费领一个AWS、华为云等的主机

【1024 程序员】我劝你赶紧去免费领一个 AWS、华为云等的主机 每年 10 月 24 日,程序员们都会迎来...
星哥带你玩飞牛NAS-4:飞牛NAS安装istore旁路由,家庭网络升级的最佳实践

星哥带你玩飞牛NAS-4:飞牛NAS安装istore旁路由,家庭网络升级的最佳实践

星哥带你玩飞牛 NAS-4:飞牛 NAS 安装 istore 旁路由,家庭网络升级的最佳实践 开始 大家好我是...
告别Notion焦虑!这款全平台开源加密笔记神器,让你的隐私真正“上锁”

告别Notion焦虑!这款全平台开源加密笔记神器,让你的隐私真正“上锁”

  告别 Notion 焦虑!这款全平台开源加密笔记神器,让你的隐私真正“上锁” 引言 在数字笔记工...
星哥带你玩飞牛NAS-16:飞牛云NAS换桌面,fndesk图标管理神器上线!

星哥带你玩飞牛NAS-16:飞牛云NAS换桌面,fndesk图标管理神器上线!

  星哥带你玩飞牛 NAS-16:飞牛云 NAS 换桌面,fndesk 图标管理神器上线! 引言 哈...

免费图片视频管理工具让灵感库告别混乱

一言一句话
-「
手气不错
星哥带你玩飞牛NAS-16:不再错过公众号更新,飞牛NAS搭建RSS

星哥带你玩飞牛NAS-16:不再错过公众号更新,飞牛NAS搭建RSS

  星哥带你玩飞牛 NAS-16:不再错过公众号更新,飞牛 NAS 搭建 RSS 对于经常关注多个微...
手把手教你,购买云服务器并且安装宝塔面板

手把手教你,购买云服务器并且安装宝塔面板

手把手教你,购买云服务器并且安装宝塔面板 前言 大家好,我是星哥。星哥发现很多新手刚接触服务器时,都会被“选购...
一句话生成拓扑图!AI+Draw.io 封神开源组合,工具让你的效率爆炸

一句话生成拓扑图!AI+Draw.io 封神开源组合,工具让你的效率爆炸

一句话生成拓扑图!AI+Draw.io 封神开源组合,工具让你的效率爆炸 前言 作为天天跟架构图、拓扑图死磕的...
安装Black群晖DSM7.2系统安装教程(在Vmware虚拟机中、实体机均可)!

安装Black群晖DSM7.2系统安装教程(在Vmware虚拟机中、实体机均可)!

安装 Black 群晖 DSM7.2 系统安装教程(在 Vmware 虚拟机中、实体机均可)! 前言 大家好,...
150元打造低成本NAS小钢炮,捡一块3865U工控板

150元打造低成本NAS小钢炮,捡一块3865U工控板

150 元打造低成本 NAS 小钢炮,捡一块 3865U 工控板 一块二手的熊猫 B3 工控板 3865U,搭...