
数据库版本: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;
然后接着再插入一些数据进去

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

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

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

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


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

官方文档给出,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表留有一部分空闲空间没有释放给操作系统,那么我们再插入一些数据,观察下情况


那么索引膨胀的问题要如何处理呢?
这里想让索引idx_dt_t2空间得到释放,需要对索引进行重建,重建后,该索引的空闲空间才能得到释放

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



从官方文档上了解到,对索引做reindex,实际就是删除老的索引,新建一个索引替换掉老的,在此期间,会对索引加上排他锁,阻塞在其上读和写的事务,在业务高峰期时影响比较大;官方给的另外一种方式,是手动删除索引,再创建索引使用concurrently,其实在删除之后,会导致全表扫描,但不至于阻塞事务,反正总归还是会有一点点影响,为了最大限度降低影响,要么选择业务低峰像晚上处理,或是停机处理更佳。
文章转载自开源软件联盟PostgreSQL分会,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




