版本 - PostgreSQL 10.21,由 Visual C++ build 1800 编译,64 位
平台 - Windows
将数据从 PostgreSQL 10.21 迁移到 14.7 时,我们在转储过程中遇到与表损坏相关的问题,导致错误消息“无法打开文件“base/16510/37857”:没有这样的文件或目录。”
错误:无法打开文件“base/16510/37857”:没有这样的文件或目录
检查“relfilenode”的“relkind”后,确定类型为toast table。在尝试恢复数据时,我们最初尝试截断 toast 表,但遇到了阻止成功截断的错误。作为解决方法,我们在目录“base/16510”下创建了一个名为“37857”的新空文件。然而,这导致了一个新错误:“pg_toast_37722 中的 toast 值 15977662 缺少块编号 0。”
错误:pg_toast_37722 中的 toast 值 15977662 缺少块编号 0
ProdDB=# select count(*) from prrhtab;
count
--------
232966
(1 row)
ProdDB=# select * from prrhtab;
ERROR: could not open file "base/16510/37857": No such file or directory
ProdDB=# select relname, relkind from pg_class where relfilenode=37857;
relname | relkind
----------------+---------
pg_toast_37722 | t
(1 row)
ProdDB=# select * from pg_toast.pg_toast_37722;
ERROR: could not open file "base/16510/37857": No such file or directory
ProdDB=# select count(*) from pg_toast.pg_toast_37722;
ERROR: could not open file "base/16510/37857": No such file or directory
ProdDB=# truncate table pg_toast.pg_toast_37722;
ERROR: "pg_toast_37722" is not a table
ProdDB=# select count(*) from pg_toast.pg_toast_37722;
count
-------
0
(1 row)
ProdDB=# select * from prrhtab;
ERROR: could not read block 2635 in file "base/16510/37857": read only 0 of 8192 bytes
ProdDB=# reindex table prrhtab;
REINDEX
ProdDB=# select * from prrhtab;
ERROR: missing chunk number 0 for toast value 15977662 in pg_toast_37722
ProdDB=# select count(*) from prrhtab;
count
--------
232966
(1 row)
ProdDB=# select * from prrhtab order by id desc limit 1;
id
---------
1177027
(1 row)
为了解决此问题,我们继续逐一删除损坏的行,结果,“缺少块编号”错误不再发生。
不幸的是,我们没有表损坏发生之前的备份。虽然我们能够使用上述方法继续进行迁移,但我们不确定是否有其他方法可以解决此问题而不丢失任何数据。此外,逐一检查行以解决“缺少块编号”错误的过程似乎效率不高。因此,我们将不胜感激任何有关最佳解决方案的建议以及可能的原因,以便将来避免类似问题。
参考了下面的线程,但是在处理较高的行数时似乎很耗时。
您以正确的方式解决了问题。
什么可能帮助您找到有问题的行:
DO
$$DECLARE
v_pk bigint; -- or whatever type your primary key is
v_t text;
BEGIN
FOR pk IN SELECT id FROM prrhtab LOOP
BEGIN
SELECT prrhtab::text INTO v_t FROM prrhtab WHERE id = v_pk;
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE 'Broken row with id = %', v_pk; -- or go ahead and delete it
END;
END LOOP;
END;$$;