原文地址:
https://www.cybertec-postgresql.com/en/orphaned-files-after-postgresql-crash/

PostgreSQL 以其稳定性而闻名,这毋庸置疑。但这并不意味着它永远不会崩溃。虽然 PostgreSQL 通常会自行清理,但在崩溃后它并没有很好的清理方法(毕竟它丢失了内存)。因此,你的数据目录中可能会出现一些孤立的文件。如果这些文件很小,你可能不会担心。但有时你的数据目录中可能会出现很多垃圾文件。众所周知,解决这个问题非常困难,所以我决定写一篇文章来解决这个问题。
PostgreSQL 为何会崩溃?
如果本文标题让您对 PostgreSQL 产生不好的印象,我会尽力消除您的这种感觉。PostgreSQL 很少崩溃。但有时崩溃并非 PostgreSQL 的错。让我列举一些可能导致崩溃的原因:
你没有禁用内存过量提交,导致内存不足,并且 Linux 内核的内存不足终止程序向其中一个 PostgreSQL 进程发送了 SIGKILL
https://www.cybertec-postgresql.com/en/what-you-should-know-about-linux-memory-overcommit-in-postgresql/
包含
pg_wal
的文件系统已满, PostgreSQL 无法创建新的WAL段https://www.cybertec-postgresql.com/en/why-does-my-pg_wal-keep-growing/
PostgreSQL 进程在“关键部分”(修改数据但尚未达到一致性)遇到无法处理的错误 — PostgreSQL 将此类错误升级为
PANIC内存、磁盘或其他硬件组件故障导致数据损坏
管理员无意中损坏了数据库
https://www.cybertec-postgresql.com/en/how-to-corrupt-your-postgresql-database/
PostgreSQL 软件错误导致崩溃或数据损坏(您是否正在运行不受支持的版本或未更新最新的次要版本?)
https://www.cybertec-postgresql.com/en/upgrading-and-updating-postgresql/
事实上,导致崩溃并留下大量孤立文件的最常见原因是用户运行VACUUM (FULL)
。此命令会创建表及其所有索引的副本。如果磁盘空间不足,而 PostgreSQL 无法创建新的WAL段,数据库服务器就会崩溃。这样的崩溃会留下未完全创建的表副本。为了避免此类问题,我建议您将pg_wal
和数据目录放在不同的文件系统上(同时,也请将日志文件放在单独的文件系统上)。
为什么崩溃会留下孤立文件?
您可能知道 PostgreSQL在崩溃时无法清理,但为什么它在重启时无法清理呢?对此我有两个答案:
遍历所有数据库目录中的所有文件并将其与目录数据和
pg_filenode.map
进行比较,这可能需要很长时间,在此期间服务器尚不可用目录表中的数据损坏可能导致 PostgreSQL 删除合法数据文件,这将加剧数据损坏
我认为第二点是放弃任何自动清理活动的一个很好的理由。
崩溃后如何识别并删除孤立文件?
这是关键问题。我提供两种方法:
安全的方法:转储并恢复
处理该问题的简单、安全且受支持的方法是:
停止应用程序
pg_dump
数据库到磁盘DROP DATABASE
— 这将删除所有文件,包括孤立文件创建一个新的空数据库
将转储恢复到新数据库
启动应用程序
顺便说一句,这也是清理经历过任何形式数据损坏的数据库的正确方法。
虽然这种方式安全又简单,但清理大型数据库可能会导致严重的宕机。如果您觉得无法承受,并且不想采取下文所述的风险路径,那么您应该接受这些孤立文件。它们不会造成任何损害,只会占用部分磁盘空间,而且如今磁盘空间通常比长时间宕机要便宜得多。
危险的方法:识别并删除孤立文件
有几个PostgreSQL 函数允许超级用户检查文件系统:
https://www.postgresql.org/docs/current/functions-admin.html
| 功能 | 描述 |
|---|---|
pg_ls_dir(dirname) | 列出数据库服务器上的目录,返回一个text表 |
pg_stat_file(filename) | 返回包含数据库服务器文件各种信息的复合记录,包括大小和修改时间戳 |
pg_filenode_relation(tablespace, filenode) | 返回属于数据库目录中某个文件的关系(表、索引、序列等)的对象 ID(如果不存在这样的关系,则返回 NULL) |
通过了解 PostgreSQL 的内部结构,我们可以编写一个针对数据库目录中所有孤立文件的查询:
WITH d AS (/* default tablespace directory for the database */SELECT current_setting('data_directory') ||'/base/' ||(SELECT oid FROM pg_databaseWHERE datname = current_database()) AS path)SELECT split_part(dir.f, '.', 1) AS filenode,/* relation size is the sum of all segments */pg_size_pretty(sum(f.size)) AS size,max(f.modification) AS last_modifiedFROM d/* all the files in the database directory */CROSS JOIN LATERAL pg_ls_dir(d.path) AS dir(f)/* file data like size and modification timestamp */CROSS JOIN LATERAL pg_stat_file(d.path || '/' || dir.f) AS fWHERE /* file name is digits only, optionally followed by a dot and more digits *//* that means: a segment of the "main fork" of a relation */f ~ '^\d+(\.\d+)*$'/* PostgreSQL doesn't know a relation that belongs to the file */AND pg_filenode_relation(0::oid, split_part(dir.f, '.', 1)::bigint::oid) IS NULLGROUP BY filenode;
SELECT current_setting('data_directory') ||'/base/' ||(SELECT oid FROM pg_databaseWHERE datname = current_database());
rm 12345*
关于删除孤立文件的警告
删除文件时要小心:
上述查询仅会识别默认表空间中的文件。如果您创建了其他表空间,则查询将无法在这些表空间中找到孤立文件。
不要删除最近的文件。并发事务可能已经创建了这些文件,而目录条目尚未对您的查询可见。
最好在崩溃发生后等待一两周。然后,仅删除查询返回的、在崩溃时最后修改的文件。
如果查询返回一些小文件,请不要删除它们。它们不会给您带来麻烦,也不值得冒险。
在删除数据目录中的任何文件之前,请确保您已做好备份。
结论
崩溃后清除孤立文件的安全方法是转储并恢复数据库。对于那些胆子更大的朋友,我提供了一个查询,可以帮助你识别这些孤立文件。




