暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

最佳实践:PostgreSQL空间回收操作


操作系统:Centos 7.4
数据库版本:PostgreSQL 10.3
首先创建一张表t2,增加一个索引 idx_dt_t


我们看下新建的对象大小,先查出表、序列和索引对应的oid
查看当前表的大小,序列大小,索引大小


然后查看下所在数据库的oid



再去目录中查看下对应的物理文件,这里查到
1、序列t2_id_seq对应物理文件24598,初始大小8k;
2、表t2对应物理文件24600,初始大小0;
3、索引idx_dt_t2对应物理文件24604,初始大小8k;



现在我们往表t2插入一些数据,观察下表及索引的大小变化



这里插入了10000条数据,观察下表和索引的大小


这里看表t2通过两种方式查看空间,结果一个是384k,一个是360k,差了24k,这里360k指是表数据所占空间,另外24k是系统给表t2增加的一个fsm文件,全称叫空闲空间映射(free space mapping),有兴趣的小伙伴可以去了解下,在这里就不做深入了,我们去看下文件的实际大小


这里看到t2对应的文件24600大小360k,索引idx_dt_t2对应的文件24604大小296k,以及系统生成的fsm文件大小24k;

然后接着再插入一些数据进去

这里陆续插入了7次数据,表的大小2480k,索引的大小1928k,这里看到索引大小几乎赶上表的大小 ,原因是表本身只有两个字段,而且索引还是针对dt字段的;到目前为止,对于表t2都只是做插入操作,所以t2表对应的存储块上应该没有空闲空间;

我们先来看下t2的数据情况,目前该表占用了310个块,没有空闲空间


索引idx_dt_t2占用了241个块,也没有空闲空间



然后我来删除20220101的数据,观察下表和索引的变化,以及空间的使用情况

这里我们删除了日期为‘20220101’的一万条数据,看到表和索引的大小依然没有变化,即使使用analyze收集了统计信息,使用空间也没有变化;

同时我们再看下表的数据以及对应物理文件的大小


这里我们看到物理文件大小也是没有变化 ;

然后我们对表手工做一次vacuum处理,观察下表的变化


这里看到,对表t2做了 vacuum 处理后,表空间和索引占用空间依然没有变化,但是对于表上使用的数据块出现了变化,存在12.69%的空闲空间;理论上来说,表t2刚刚删除了1/7的数据,部分数据块上也应该空出来了,只是没有被系统回收,这个和oracle表上删除部分数据一样,删除数据的空间还保留在该表上,以备后面进来的数据使用;但是索引的空间并没有腾出来,这就存在一个潜在的风险了,如果表上的数据在经过多次修改删除,索引的使用空间会无限膨胀,导致空间浪费的问题;

官方文档给出,VACUUM用来收回由死亡元组占用的存储空间。在通常PostgreSQL操作中,被删除或者被更新废弃的元组并没有在物理上从它们的表中移除,它们将一直存在直到一次VACUUM被执行。因此有必要周期性地做VACUUM,特别是在频繁被更新的表上。

简单的 VACUUM(不带FULL)简单地收回空间并使其可以被重用。这种形式的命令可以和表的普通读写操作并行,因为它不会获得一个排他锁。但是,这种形式中额外的空间并没有被还给操作系统(在大多数情况下),它仅仅被保留在同一个表中以备重用。

前面说的是手动调用vacuum来回收空间,PostgreSQL实际上提供定期执行vacuum的进程:
autovacuum。autovacuum daemon周期性的调用多个autovacuum_worker进程。默认情况下, 它每1分钟唤醒一次(由autovacuum_naptime定义), 并调用三个work(默认由autovacuum_max_works指定)。
在数据库影响最小的时候,由autovacuum调用的autovacuum work对每个的表逐步同时进行vacuum处理。

总之,即使我们没有手动对表进行vacuum处理,系统也会根据数据库配置自动做vacuum处理,处理的频率取决于数据库的几个配置参数:
#autovacuum = on   
#autovacuum_vacuum_threshold = 50       # min number of row updates before vacuum
#autovacuum_vacuum_scale_factor = 0.2   # fraction of table size before vacuum
当表更新或者删除的元组数超过autovacuum_vacuum_threshold+autovacuum_vacuum_scale_factor*table_size会触发vacuum,这两个参数每个表可以单独设置。

前面我们看到t2表留有一部分空闲空间没有释放给操作系统,那么我们再插入一些数据,观察下情况

这里我们插入了20220801的一万条数据,看到表t2的空间还是2480k,没有增加,但索引idx_dt_t2的空间从之前的1928k增长到2200k,也印证了我们刚刚的担忧;这里也看表t2的空闲块,只有0.16%空闲,几乎占满了,说明表上的空闲空间被重复利用了;

那么索引膨胀的问题要如何处理呢?

这里想让索引idx_dt_t2空间得到释放,需要对索引进行重建,重建后,该索引的空闲空间才能得到释放

从上图中可看到,重建后,该索引由原来的2200k,降到了1552k,确实释放了一部分空间。

再看下索引对应的物理文件,先查下索引对应的oid,再看下物理文件

上面我们查到索引的oid是24604,但是对应物理文件中找不到24604,却多了一个24606文件,那这个24606文件是不是索引对应的物理文件呢,我们可以用另外一个函数查看下索引对应的物理文件

果然,索引的物理文件变成了24606,这里也说明对象的oid不一定等于对象的文件名,当对象重建时,会发生变化;

从官方文档上了解到,对索引做reindex,实际就是删除老的索引,新建一个索引替换掉老的,在此期间,会对索引加上排他锁,阻塞在其上读和写的事务,在业务高峰期时影响比较大;官方给的另外一种方式,是手动删除索引,再创建索引使用concurrently,其实在删除之后,会导致全表扫描,但不至于阻塞事务,反正总归还是会有一点点影响,为了最大限度降低影响,要么选择业务低峰像晚上处理,或是停机处理更佳。

文章转载自开源软件联盟PostgreSQL分会,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论