PostgreSQL 教程: 处理数据块损坏

三月 20, 2024

摘要:在本教程中,您将学习如何处理 PostgreSQL 中损坏的数据块。

介绍

在极罕见的情况下,某个表中的一些数据块发生损坏,这可能是由于某些硬件或文件系统问题造成的。如果您遇到如下错误消息,则表示您的数据库中有一些数据块已损坏。

ERROR: invalid page in block 54206178 of relation base/16389/2825248
STATEMENT: SELECT * FROM some_table WHERE ...

处理方案

第一步是要找到需要进行修复的损坏对象。日志中给出了文件位置(实际上,有许多段文件对应给定的路径,您可以通过块号来找到精确的文件)。但问题是需要先确定这个文件对应什么对象。在上面的例子中,该文件是一个普通的表,但在你知道它之前,你无法修复它。

要找到对应的数据库对象,可以使用以下 SQL:

SELECT c.oid, n.nspname AS schema, c.relname,
  CASE c.relkind
  WHEN 'r' THEN 'table'
  WHEN 'i' THEN 'index'
  WHEN 't' THEN 'TOAST table'
  WHEN 'm' THEN 'materialized view'
  WHEN 'S' THEN 'sequence'
  ELSE 'other'
  END AS type
FROM pg_class AS c
JOIN pg_namespace AS n ON c.relnamespace = n.oid
WHERE c.relfilenode = 2825248;

在 WHERE 条件中,relfilenode 编号对应于错误消息中的文件编号。这将为您输出如下结果,其中 type 字段是对象的类型,schema / relname 是损坏的对象。

   oid   | schema |  relname   | type
---------+--------+------------+-------
 2825248 | public | some_table | table
(1 row)

修复索引

如果损坏的对象是索引,这个情况还算比较好处理,因为可以在不丢失数据的情况下重建索引。以下命令就能处理这种情况:

REINDEX INDEX some_index;

修复表

如果损坏的对象是表,看来可以使用 vacuum 命令。您也可以使用zero_damaged_pages参数:当发现损坏的数据页面时,它会避免引发错误,因此处理过程不会被中断,但错误会变为警告,而且某些数据可能会丢失。

SET zero_damaged_pages TO on;

VACUUM 机制会重建表,从而清除掉数据损坏。将它与前面的参数设置结合使用,是相对合理的。让我们假定损坏的数据可以丢失。

VACUUM FULL some_table;

此命令会阻塞表的访问,创建一个新表,因此您需要有足够的可用空间来进行处理。如果您对阻塞表感到困扰,您可以考虑使用 pg_repack,以最少的锁来重新组织表。

修复 TOAST 表

对于 TOAST 表,上面的查询可能会给出如下结果:

  oid   |  schema  |     relname     |    type
--------+----------+-----------------+-------------
 124590 | pg_toast | pg_toast_124589 | TOAST table
(1 row)

然后,您需要找到 TOAST 表所属的主表:

SELECT n.nspname AS schema, c.relname
FROM pg_class AS c
JOIN pg_namespace AS n ON c.relnamespace = n.oid
WHERE c.reltoastrelid = 124590;

最后,您可以通过修复主表来修复 TOAST 表,如上所述