暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

[译] PostgreSQL 崩溃后出现的孤立文件

原创 王语嫣 2025-04-30
165

PostgreSQL 以其稳定性而闻名,这毋庸置疑。但这并不意味着它永远不会崩溃。虽然 PostgreSQL 通常会自行清理,但在崩溃后它并没有很好的清理方法(毕竟它丢失了内存)。因此,你的数据目录中可能会出现一些孤立的文件。如果这些文件很小,你可能不会担心。但有时你的数据目录中可能会出现很多垃圾文件。众所周知,解决这个问题非常困难,所以我决定写一篇文章来解决这个问题。

PostgreSQL 为何会崩溃?

如果本文标题让您对 PostgreSQL 产生不好的印象,我会尽力消除您的这种感觉。PostgreSQL 很少崩溃。但有时崩溃并非 PostgreSQL 的错。让我列举一些可能导致崩溃的原因:

  • 你没有禁用内存过量提交,导致内存不足,并且 Linux 内核的内存不足终止程序向其中一个 PostgreSQL 进程发送了 SIGKILL
  • 包含的文件系统pg_wal已满, PostgreSQL 无法创建新的WAL段
  • PostgreSQL 进程在“关键部分”(修改数据但尚未达到一致性)遇到无法处理的错误 — PostgreSQL 将此类错误升级为PANIC
  • 内存、磁盘或其他硬件组件故障导致数据损坏
  • 管理员无意中损坏了数据库
  • PostgreSQL 软件错误导致崩溃或数据损坏(您是否正在运行不受支持的版本或未更新最新的次要版本?)

事实上,导致崩溃并留下大量孤立文件的最常见原因是用户运行VACUUM (FULL)。此命令会创建表及其所有索引的副本。如果磁盘空间不足,而 PostgreSQL 无法创建新的WAL段,数据库服务器就会崩溃。这样的崩溃会留下未完全创建的表副本。为了避免此类问题,我建议您将pg_wal和数据目录放在不同的文件系统上(同时,也请将日志文件放在单独的文件系统上)。

为什么崩溃会留下孤立文件?

您可能知道 PostgreSQL在崩溃时无法清理,但为什么它在重启时无法清理呢?对此我有两个答案:

  • 遍历所有数据库目录中的所有文件并将其与目录数据进行比较,这pg_filenode.map可能需要很长时间,在此期间服务器尚不可用
  • 目录表中的数据损坏可能导致 PostgreSQL 删除合法数据文件,这将加剧数据损坏

我认为第二点是放弃任何自动清理活动的一个很好的理由。

崩溃后如何识别并删除孤立文件?

这是关键问题。我提供两种方法:

安全的方法:转储并恢复

处理该问题的简单、安全且受支持的方法是:

  • 停止应用程序
  • pg_dump数据库到磁盘
  • DROP DATABASE— 这将删除所有文件,包括孤立文件
  • 创建一个新的空数据库
  • 将转储恢复到新数据库
  • 启动应用程序

顺便说一句,这也是清理经历过任何形式数据损坏的数据库的正确方法。

虽然这种方式安全又简单,但清理大型数据库可能会导致严重的宕机。如果您觉得无法承受,并且不想采取下文所述的风险路径,那么您应该接受这些孤立文件。它们不会造成任何损害,只会占用部分磁盘空间,而且如今磁盘空间通常比长时间宕机要便宜得多。

危险的方法:识别并删除孤立文件

有几个PostgreSQL 函数允许超级用户检查文件系统:

精选 PostgreSQL 文件信息函数

功能 描述
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_database WHERE 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_modified FROM 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 f WHERE /* 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 NULL GROUP BY filenode;

要删除查询返回的文件节点(假设我们得到 12345),请转到数据库目录:

SELECT current_setting('data_directory') || '/base/' || (SELECT oid FROM pg_database WHERE datname = current_database());

并删除属于它的文件:

rm 12345*

关于删除孤立文件的警告

删除文件时要小心:

  • 上述查询仅会识别默认表空间中的文件。如果您创建了其他表空间,则查询将无法在这些表空间中找到孤立文件。
  • 不要删除最近的文件。并发事务可能已经创建了这些文件,而目录条目尚未对您的查询可见。
  • 最好在崩溃发生后等待一两周。然后,仅删除查询返回的、在崩溃时最后修改的文件。
  • 如果查询返回一些小文件,请不要删除它们。它们不会给您带来麻烦,也不值得冒险。
    在删除数据目录中的任何文件之前,请确保您已做好备份。

结论

崩溃后清除孤立文件的安全方法是转储并恢复数据库。对于那些胆子更大的朋友,我提供了一个查询,可以帮助你识别这些孤立文件。

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

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论