Postgres 日志文件包含:pg_toast_2619 中的 toast 值 815441 缺少块号 0

Posted

技术标签:

【中文标题】Postgres 日志文件包含:pg_toast_2619 中的 toast 值 815441 缺少块号 0【英文标题】:Postgres log file contains: missing chunk number 0 for toast value 815441 in pg_toast_2619 【发布时间】:2017-11-28 14:12:01 【问题描述】:

以下日志消息在 postgres 日志文件中可用数千次。如何解决。

pg_toast_2619 中的 toast 值 815441 缺少块号 0。

pg_toast_2619 是 pg_statistic 表。它(pg_statistic)也包含重复的记录。如何解决这种情况。这背后的原因是什么。

【问题讨论】:

【参考方案1】:

您的服务器出了点问题。服务器崩溃了?磁盘故障? 无论如何你都可以这样做:

    停止服务器并制作数据目录的物理副本以 一个安全的地方; 由于 pg_statistic 由 ANALYZE 填充,只需清理它 DELETE FROM pg_catalog.pg_statistic; 并随后发出 ANALYZE

如果错误仍然存​​在:

    启用 allow_system_table_mods 然后重启你的服务器:ALTER SYSTEM SET allow_system_table_mods = ON; (Postgres 9.4+) 截断您遇到错误的数据库的 pg_statistic:TRUNCATE TABLE pg_catalog.pg_statistic; 再次分析整个数据库:ANALYZE VERBOSE; 禁用allow_system_table_mods:ALTER SYSTEM RESET allow_system_table_mods;

完成此操作后您可能需要REINDEX SYSTEM

关于allow_system_table_mods here的更多信息。

【讨论】:

这个问题是否只存在于 Postgres 9.2 中? 我看到发生在 9.5 集群中。但在那种情况下,问题出在磁盘上……与 Postgres 本身无关,pg_statistic 损坏只是后果。 感谢您的回答,这对您有很大帮助!我尝试了所有方法,但没有意识到我可以从 pg_catalog.pg_statistic 表中删除。 前两个步骤对我有用,谢谢@MichelMilezzi【参考方案2】:

假设损坏的表名为mytable。 Internet 上的许多文章都建议对数据库执行以下查询:

psql> select reltoastrelid::regclass from pg_class where relname = 'mytable';

 reltoastrelid      
-------------------------
 pg_toast.pg_toast_40948
(1 row)

然后触发以下命令:

REINDEX table mytable;
REINDEX table pg_toast.pg_toast_40948;
VACUUM analyze mytable;

但就我而言,这还不够。 然后,我计算了mytable中的行数:

psql> select count(*) from mytable;

 count
-------
 58223

要查找损坏,可以从表中获取数据,直到出现 'Missing chunk...' 错误。因此,以下一组查询可以完成这项工作:

select * from mytable order by id limit 5000 offset 0;
select * from mytable order by id limit 5000 offset 5000;
select * from mytable order by id limit 5000 offset 10000;
select * from mytable order by id limit 5000 offset 15000;
select * from mytable order by id limit 5000 offset 20000;
...

...依此类推,直到出现错误。在此示例中,如果您达到 55000 的偏移量(55000 + 5000 是 60000,超过了记录总数)而没有收到错误,那么您的表没有损坏。 order by 子句是使您的查询可重复所必需的,即确保查询不会随机返回行,并且 limitoffset 子句按预期工作。如果您的表没有id 字段,您必须找到一个好的字段来排序。出于性能原因,最好选择索引字段。

为了更快不弄脏你的控制台,可以直接从控制台触发查询,将输出重定向到/dev/null并仅在发现错误时打印错误消息:

psql -U pgsql -d mydatabase -c "select * from mytable order by id limit 5000 offset 0" > /dev/null || echo "Corrupted chunk read!"

上述语法的意思是:执行查询并将输出重定向到/dev/null,或者,如果出现错误(||),则写入错误消息。

假设第一个给出错误的查询如下:

select * from mytable order by id limit 5000 offset 10000;
Corrupted chunk read!
>

现在,您知道损坏的块位于 10000 到 14999 之间的行中。因此,您可以通过将查询 LIMIT 子句减半来缩小搜索范围。

select * from mytable order by id limit 2500 offset 10000;
Corrupted chunk read!
>

因此,错误恰好出现在 10000 到 12499 之间的行中。我们再次将行数限制减半。

select * from mytable order by id limit 1250 offset 10000;
>

获取 10000 到 12499 之间的行不会返回任何错误。所以错误必须在 11250 和 12499 之间的行中。我们可以通过触发查询来确认这一点:

select * from mytable order by id limit 1250 offset 11250;
Corrupted chunk read!
>

所以,我们再次将限制减半。

select * from mytable order by id limit 625 offset 11250;
>
select * from mytable order by id limit 625 offset 11875;
Corrupted chunk read!
>
...

您应该继续缩小范围,直到准确找到损坏的行:

...
select * from mytable order by id limit 1 offset 11963;
Corrupted chunk read!
>

请注意,在最后一个查询中,LIMIT 1 子句仅准确标识一行。

最后,你必须找到损坏行的 id 并将其删除(显然你有数据丢失):

psql> select id from mytable order by id limit 1 offset 11963;
   id
--------
 121212

psql> delete from mytable where id = 121212;
DELETE 1
>

在搜索损坏的行期间,请考虑到,损坏很可能出现在最后插入/更新的记录中,即使这不是一般规则。因此,您可以选择一个尊重物理插入/更新的排序键,以减少扫描时间。

如果您希望完全自动化损坏的行搜索,请考虑使用以下脚本(在 csh 语法中):

#!/bin/csh
set j = 0
while ($j < 58223) //here the total number of table rows
  psql -U pgsql -d mydatabase -c "SELECT * FROM mytable LIMIT 1 offset $j" >/dev/null || echo $j
  @ j++
end

此脚本打印所有损坏行的数量。在长表的情况下,它可能需要很长时间,因为它执行的查询与表的行数一样多。

我在我的要点here 中发布了同样的问题。

【讨论】:

以上是关于Postgres 日志文件包含:pg_toast_2619 中的 toast 值 815441 缺少块号 0的主要内容,如果未能解决你的问题,请参考以下文章

使用hive重定向导出的数据文件中包含WARN日志记录问题

日志分析_使用shell完整日志分析案例

在 postgres 上缓慢选择不同的查询

postgres流复制环境下pg_xlog日志优雅的清理

postgres 9.6.5 重建控制文件

在 Postgres 的单个索引中包含多个列