PG数据库报错invalid page in block

1,811 阅读3分钟

这是我参与8月更文挑战的第28天,活动详情查看:8月更文挑战

在正常执行查询语句时,出现报错如下:

postgres=# select * from tbl_index;

2019-06-22 10:27:28.966 CST [9631] ERROR:  invalid page in block 11 of relation base/13287/16384

2019-06-22 10:27:28.966 CST [9631] STATEMENT:  select * from tbl_index;

ERROR:  invalid page in block 11 of relation base/13287/16384

问题原因一般是由于磁盘坏道或者是内存问题等硬件上的原因,有时候会导致数据库的数据文件的一些数据块的损坏,使得某些表不能正常访问。 这个错误是指数据所在的目录下面base子目录,oid为13287的数据库,表的文件id为16384的表(即上述例子的tbl_index)的第11页(注意是从0页开始)数据块的头出现了错误,所以数据表不能访问。

数据块的损坏的情形比较复杂,最好的情况是丢失一个数据块里面的所有记录,最坏也有可能整个表丢失。

解决问题的方法有两种:

  1. 使用备份恢复数据库!如果有做备份和日志归档,出现问题以后恢复到最新即可。
  2. 如果不存在备份,或者没有其他办法能够从损坏的页面中恢复数据,那么只能尽力保证将表中能获取到的数据备份出来。

首先介绍一个参数:

zero_damaged_pages (boolean)

检测到一个损坏的页面头部通常会导致PostgreSQL报告一个错误,并且中止当前事务。把zero_damaged_pages设置为打开会让系统报告一个警告、把损坏的页面填充零,然后继续处理。这种行为会毁掉数据,即损坏页面上的所有行。但是它允许你绕开错误并且从表中的任何未损坏页面中检索行。

如果由于一次硬件或软件错误而发生毁坏,这种方法可用于恢复数据。通常你不应该把它设置为打开,除非你已经彻底放弃从表的损坏页面中恢复数据。

被填充零的页面不会被强制写到磁盘上,因此我们推荐在再次关闭这个参数之前先重建表或索引。默认的设置是off,并且只有超级用户可以改变它。

alter system set zero_damaged_pages = on ;

修改完上述参数后,重启数据库。

设置完成后,当访问表的时候,会提示已经忽略损坏的页面:

postgres=# select count(*) from tbl_index;
2019-06-22 10:44:07.924 CST [9868] WARNING:  invalid page in block 11 of relation base/13287/16384; zeroing out page
2019-06-22 10:44:07.924 CST [9868] WARNING:  invalid page in block 12 of relation base/13287/16384; zeroing out page
…………
 count
-------
  4963
(1 row)

该表原有1W条记录,由于页面损坏,丢失了5000多条记录。表可以访问后,立刻将表备份并在其他服务器进行还原。当然如果有其他外部约束的话,相关的表和索引也要处理。这种方法不会对物理文件作修改,只是把内存上,损坏页面的缓存变为0。

上述方法有很大的局限性会导致部分数据丢失,并且丢失数据量无法估算。建议在没有其他有效措施的情况下谨慎选择该方式。