创建一个测试表
postgres=# create table t1(a int);
CREATE TABLE
postgres=# select pg_relation_filepath('t1');
pg_relation_filepath
----------------------
base/75062/75297
(1 row)
postgres=#
在操作系统上已经可以看到该文件。
$ ls -la $PGDATA/base/75062/75297
-rw------- 1 postgres postgres 0 Nov 9 11:11 /data/pgdata/11/data/base/75062/75297
插入一些数据:
postgres=# show segment_size;
segment_size
--------------
1GB
(1 row)
postgres=# insert into t1 select * from generate_series(1,100000000);
INSERT 0 100000000
postgres=#
因为segment_size的设置为1GB,磁盘上已经有了多个文件
$ ls -la $PGDATA/base/75062/75297*
-rw------- 1 postgres postgres 1073741824 Nov 9 11:19 /data/pgdata/11/data/base/75062/75297
-rw------- 1 postgres postgres 1073741824 Nov 9 11:17 /data/pgdata/11/data/base/75062/75297.1
-rw------- 1 postgres postgres 1073741824 Nov 9 11:18 /data/pgdata/11/data/base/75062/75297.2
-rw------- 1 postgres postgres 439803904 Nov 9 11:19 /data/pgdata/11/data/base/75062/75297.3
-rw------- 1 postgres postgres 917504 Nov 9 11:18 /data/pgdata/11/data/base/75062/75297_fsm
现在,开启另一个会话(session 2)。
在session2中,启动一个事务并创建一个空表,但是不提交事务:
postgres=# begin;
BEGIN
postgres=# create table t2(a int);
CREATE TABLE
postgres=# select pg_relation_filepath('t2');
pg_relation_filepath
----------------------
base/75062/75300
(1 row)
postgres=# select * from pg_backend_pid();
pg_backend_pid
----------------
17710
(1 row)
postgres=#
在操作系统已经可以看到对应的文件:
$ ls -la $PGDATA/base/75062/75300
-rw------- 1 postgres postgres 0 Nov 9 11:23 /data/pgdata/11/data/base/75062/75300
如果这个时候,posrgresql server发生了奔溃、或者发生了oom被kill了或者session被kill了。会发生什么呢?
我们来模拟一下session被kill的场景:
$ kill -9 17710
再次在session2中执行查询:
postgres=# select 1;
server closed the connection unexpectedly
This probably means the server terminated abnormally
before or while processing the request.
The connection to the server was lost. Attempting reset: Succeeded.
postgres=#
这个session在事务提交之前被kill了,事务无法正常完成,但是事务已经创建了一个表。应该发生什么呢?事务被回滚,创建的表应该不存在了。
postgres=# select * from t2;
ERROR: relation "t2" does not exist
LINE 1: select * from t2;
^
postgres=#
这正是我们所预期的。但在操作系统上,文件仍然存在:
$ ls -la $PGDATA/base/75062/75300
-rw------- 1 postgres postgres 0 Nov 9 11:23 /data/pgdata/11/data/base/75062/75300
这样,文件就成了孤儿文件(orphaned file)。
postgresql并不知道这个文件属于哪个relation
postgres=# select relname from pg_class where oid = '75300';
relname
---------
(0 rows)
postgres=#
这样,你就需要自己手动清理孤儿文件了!
假设你做了大量的数据的加载,就在加载完成之前,会话被杀死:
postgres=# begin;
BEGIN
postgres=# create table t3(a int);
CREATE TABLE
postgres=# select pg_relation_filepath('t3');
pg_relation_filepath
----------------------
base/75062/99528
(1 row)
postgres=# select * from pg_backend_pid();
pg_backend_pid
----------------
21988
(1 row)
postgres=# insert into t3 select * from generate_series(1,1000000000);
server closed the connection unexpectedly
This probably means the server terminated abnormally
before or while processing the request.
The connection to the server was lost. Attempting reset: Failed.
虽然会话被kill了。但是磁盘上的空间并没有被释放。
$ ls -la $PGDATA/base/75062/99528*
-rw------- 1 postgres postgres 1073741824 Nov 9 11:51 /data/pgdata/11/data/base/75062/99528
-rw------- 1 postgres postgres 413777920 Nov 9 11:51 /data/pgdata/11/data/base/75062/99528.1
-rw------- 1 postgres postgres 385024 Nov 9 11:51 /data/pgdata/11/data/base/75062/99528_fsm
在最糟糕的时候,可能会占用大量的磁盘空间。那是否有什么方法去检测这些孤儿文件呢?
你需要比较postgresql中的目录表中的记录和文件系统上信息,然后删除这些孤儿文件。这个过程需要小心谨慎。
首先获得你要检测的数据库的oid:
postgres=# select oid from pg_database where datname = 'postgres';
oid
-------
75062
(1 row)
postgres=#
这样就可以知道文件在文件系统上的位置。即 $PGDATA/base/[OID_OF_THE_DATABASE]
然后,获得孤儿文件:
postgres=# select * from pg_ls_dir ( '/data/pgdata/11/data/base/75062' ) as file where file ~ '^[0-9]*$' and file::text not in (select oid::text from pg_class );
file
-------
75280
75281
75282
75283
75300
83144
99528
(7 rows)
postgres=#