如何回收 PostgreSQL 上的磁盘空间?

30 postgresql disk-space

我在本地安装了 9.1 数据库,其中有几个表有 cca​​。300 条 mio 记录,数据库增长到大约 20 GB。之后我发出delete from命令从中删除所有记录(我应该使用truncate,但我不知道)。所以我对我的数据库进行了完全真空以回收磁盘空间,但这无济于事。我的问题看起来与这个相同,但没有提供解决方案。我已经检查了这个线程和关于“恢复磁盘空间”的文档,但仍然找不到解决方案。我使用此代码获取所有表的大小

 SELECT nspname || '.' || relname AS "relation",
 pg_size_pretty(pg_total_relation_size(C.oid)) AS "total_size"
 FROM pg_class C
 LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
 WHERE nspname NOT IN ('pg_catalog', 'information_schema')
 AND C.relkind <> 'i'
 AND nspname !~ '^pg_toast'
 ORDER BY pg_total_relation_size(C.oid) DESC
 LIMIT 15;
Run Code Online (Sandbox Code Playgroud)

但是总共不到 1GB

SELECT pg_database.datname, pg_size_pretty(pg_database_size(pg_database.datname)) AS size FROM pg_database 
Run Code Online (Sandbox Code Playgroud)

仍然显示大约 20 GB。任何建议非常感谢。

小智 29

虽然您没有说明,但我从您对您遵循的文档的引用中假设您已经对数据库和/或受影响的表进行了 VACUUM FULL。您也没有指定您使用的 postgresql 版本 - 我假设它 > 9.0(在此之前 VACUUM FULL 的行为有所不同)。

VACUUM FULL会将受影响的表重写为新文件,然后删除旧文件。但是,如果任何进程仍然打开旧文件,操作系统实际上不会删除该文件 - 直到最后一个进程关闭它。

如果可行,重新启动数据库将确保关闭所有打开的文件。

如果这不切实际,那么您可以验证这是否是您的问题,并找出打开了文件的进程。

如果使用 Linux(或大多数其他类 Unix 系统),您可以使用 'lsof' 命令获取在所有进程中打开的所有文件的列表。打开但已被删除的文件将在文件名后附加“(已删除)”。因此,您可以 grep lsof 的输出,查找已删除的文件,如下所示:

sudo lsof -u postgres | grep 'deleted'
Run Code Online (Sandbox Code Playgroud)

如果识别出仍然打开旧文件的进程,您可以使用 pg_terminate_backend 终止该进程:

SELECT pg_terminate_backend(xxx);
Run Code Online (Sandbox Code Playgroud)

其中 xxx 是进程的 PID,可在 lsof 输出中找到。

如果使用 Windows,则可以应用相同的原则,因为 postgres 使用 FILE_SHARE_DELETE 标志打开文件,这允许它删除在另一个进程中打开的文件。' handle ' 命令大致相当于 lsof,但我不确定您是否能判断文件是否被删除,因此可能需要一些额外的工作。

至于为什么任何此类进程会挂在旧文件句柄上,这是另一个问题。但是,在您在问题中引用的线程中,Tom Lane 似乎暗示它可能发生。