PostgreSQL 崩溃后出现表文件残留

John Doe 八月 19, 2025

摘要:在本文中,我们将了解 PostgreSQL 崩溃后出现表文件残留的问题,以及相关的解决方案。

目录

崩溃恢复场景: 用例

下面我们在 PostgreSQL 的数据库中,创建一个表,插入一些数据。

CREATE TABLE t (id integer, name text);

INSERT INTO t (id, name)
  SELECT i, repeat('Pg', 32)
    FROM generate_series(1, 1000000) AS s(i);

下面我们启动一个事务块,创建一个空表t1,复制表t创建一个新表t_copy,并查看下表文件的存储路径。

BEGIN;

CREATE TABLE t1 ();

CREATE TABLE t_copy AS TABLE t;

SELECT pg_relation_filepath(name) AS path
  FROM (VALUES ('t1'), ('t_copy')) AS t (name);
     path
--------------
 base/5/16389
 base/5/16392

让我们立即停止数据库,模拟数据库异常宕机的故障,再重新启动数据库服务:

$ pg_ctl stop --mode=immediate
$ pg_ctl start

数据库重新启动后,手动触发一次检查点:

CHECKPOINT;

检查重启前事务块中新增的表文件:

$ ls -lh base/5/{16389,16392}
-rw------- 1 postgres postgres   0 Aug 18 17:43 base/5/16389
-rw------- 1 postgres postgres 97M Aug 18 17:44 base/5/16392

崩溃恢复场景: 内部原理

下面是我们在 PostgreSQL 的事务中创建表文件时,崩溃恢复后出现表文件残留问题的内部原因:

  1. PostgreSQL 在事务运行的过程中,会在会话内存中记录创建的表文件。
  2. 当事务发生异常或者执行回滚操作时,会即时地清除这些表文件。
  3. 当事务异常中断,如:磁盘不足引发的 PANIC 错误,系统发出的 OOM 内存不足信号,或者发生软件错误的扩展插件,系统停电导致的服务器崩溃时,会话内存中记录的新增表文件会丢失,出现表文件残留的问题。

Redrock Postgres 的解决方案

Redrock Postgres 在创建表文件时,会记录撤消日志,当正在连接的会话因为某些原因被中止后,数据库重新启动后会检查这些异常中止的事务。数据库会回滚这些异常中止的事务,根据记录的撤消日志清理残留的表文件。

下面我们在 Redrock Postgres 的数据库中,创建和上面相同的表,并插入同样的数据。

CREATE TABLE t (id integer, name text);

INSERT INTO t (id, name)
  SELECT i, repeat('Pg', 32)
    FROM generate_series(1, 1000000) AS s(i);

下面我们启动一个事务块,创建一个空表t1,复制表t创建一个新表t_copy,并查看下表文件的存储路径。

BEGIN;

CREATE TABLE t1 ();

CREATE TABLE t_copy AS TABLE t;

SELECT pg_relation_filepath(name) AS path
  FROM (VALUES ('t1'), ('t_copy')) AS t (name);
     path
--------------
 base/5/16389
 base/5/16392

让我们立即停止数据库,模拟数据库异常宕机的故障,再重新启动数据库服务:

$ pg_ctl stop --mode=immediate
$ pg_ctl start

数据库重新启动后,手动触发一次检查点:

CHECKPOINT;

事务回滚的时候不会立即清理残留的表文件,表文件的实际清理操作是在检查点的过程中完成的。

检查重启前事务块中新增的表文件:

$ ls -lh base/5/{16389,16392}
ls: cannot access 'base/5/16389': No such file or directory
ls: cannot access 'base/5/16392': No such file or directory

数据库重新启动后,回滚了异常中止的事务,根据事务记录的撤消日志清理了残留的表文件。

了解更多

Redrock Postgres 文档: 回滚段