PostgreSQL中快速对系统表实现vacuum full
vacuum full会锁表,而且效率很低,在实际中不可能使用vacuum来缩小pg_class,这样会有很长的停机时间。
其实要实现vacuum full最简单的方法就是将一个表重新复制一遍,create table b as select * from a;然后再使用b表代替a表使用就可以了。
鉴于pg_class是所有表的基础,我们就算将其拷贝也无法将其取代掉。这样,我们可以以另外一种方式来实现,替换底层数据文件。由于pg_class有一个系统列,oid,这一个列我们无法简单的直接copy,所以我们采用一种迂回的方法。
1.新建一个表with oid, create table cxf with oids as select * from pg_class limit 0;
在这个表中建立跟pg_class一样的索引,因为如果我们将底层数据文件替换掉,而还是用老的索引文件的话,会错乱的
2.将整个pg_class使用copy命令导成文本(使用参数with oids)将oid也导出
3.然后将这个数据文件用copy with oids命令存入到第一步建的表中
4.停止数据库(让所有在缓存的数据全部写入到文件中)
5.替换底层的数据文件跟索引文件
6.重启数据库即可
使用这种方法使pg_class的数据文件大小从1.4G变成了63M,pg_attribute从1.5G变成602M,实现了vacuum full的效果,具体步骤如下:
1.查看一下关于pg_class的表以及索引信息
aligputf8=# select oid,relname,relfilenode from pg_class where relname like '%pg_class%';
oid | relname | relfilenode
----------+----------------------------+-------------
1259 | pg_class | 1259
2662 | pg_class_oid_index | 15687137
2663 | pg_class_relname_nsp_index | 15687138
3.创建一个表,结构跟pg_class一致,建表的时候必须加上with oids
aligputf8=# create table cxf with oids as select * from pg_class limit 0;
SELECT 0
aligputf8=# create index cxf_pg_class_oid_index on cxf(oid);
CREATE INDEX
aligputf8=# create index cxf_pg_class_relname_nsp_index on cxf(relname, relnamespace);
CREATE INDEX
创建索引,由于启动数据库的时候他会去找pg_class,然后通过索引去查找记录,所以这里我们需要重建索引,最后也一起把底层文件给覆盖掉
oid | relname | relfilenode
----------+--------------------------------+-------------
19317362 | cxf | 19317362
19317367 | cxf_pg_class_oid_index | 19317367
19317368 | cxf_pg_class_relname_nsp_index | 19317368
(3 rows)
可以看出两个表的字段信息跟字段内容是一致的
aligputf8=# select count(*) from pg_attribute where attrelid = 19317362;
count
-------
38
(1 row)
aligputf8=# select count(*) from pg_attribute where attrelid = 1259;
count
-------
38
(1 row)
4.查看pg_class现在的数据量
aligputf8=# select count(*) from pg_class;
count
--------
331799
(1 row)
5.将pg_class 导出成文件,然后再导入到cxf中
aligputf8=# copy pg_class to '/tmp/pg_class_cxf' with null as '' delimiter E'/5' oids;
COPY 331799
aligputf8=# copy cxf from '/tmp/pg_class_cxf' with null as '' delimiter E'/5' oids;
COPY 331799
aligputf8=# select count(*) from pg_class;
count
--------
331799
(1 row)
aligputf8=# explain select * from pg_class where oid = 1259;
QUERY PLAN
---------------------------------------------------------------------------------------
Index Scan using pg_class_oid_index on pg_class (cost=0.00..200.58 rows=1 width=268)
Index Cond: oid = 1259::oid
(2 rows)
8.使用同样的方法给pg_attribute,这个时候直接insert就可以了,不用copy成外部表,因为这个表没有oid。
数据量由1.5G变成602M