PG数据文件损坏了,咋损坏的?下面我来说点废话,讲讲这个事情的经过。
春节后开工时发现服务器的CPU和磁盘IO居高不下,经一番排查发现是硬盘读写很慢,停掉了所有程序后CPU下来了,但查看文件时依旧很慢。
这台服务器是旧电脑拿来做测试机,硬盘是系统分区SSD加一个数据分区HDD,所以咱合理怀疑机械盘有坏道影响了读写性能。
看了一下HDD中存的文件不是很大,尝试把文件备份到SSD时,发现PG的数据文件已经损坏了,rsync过程中不断报错。
好巧不巧我没对PG做备份,那咋办?只能尝试修复了。
在确认完其他文件未损坏后,我直接把$PGDATA目录下载到我的电脑,尝试通过WSL中的PG环境修复数据库。

上网搜索相关案例

这个时候当然是先通过AI搜索的同时还能提供解决办法,毕竟这种问题我也是第一次遇到,我首先选择了最近非常火的DeepSeek,结果没有结果,回了我一句系统繁忙。。
然后问了智谱清言,他让我直接通过pg_dump把数据导出来,胡扯了一通后,他让我通过wal记录查找checkpoint、再通过事务id恢复到最后一个checkpoint
后来再经过一番原始搜索,找到了几个供参考的帖子,咱们距离成功已经不远了。

  1. postgresql处理索引块损坏的思路与方法
  2. PostgreSQL页损坏如何修复
  3. PostgreSQL数据块损坏一例

修复过程

  1. 首先尝试1号帖子和2号帖子的方案
    原先咱们在服务器上执行SQL查询时会报这个错:无法在文件"base/17506/2703"中读取块3
    也是就是说损坏的数据库oid为17506,损坏的对象id为2703,那么直接查一下是哪个对象。
    1
    select * from pg_class where relfilenode = 2703
    这时有报错了:could not open file "base/17506/2703": No such file or directory
    这是为啥呢?因为文件已经损坏了,从服务器拉下来的时候直接跳过这个文件了,这样也没法尝试3号帖子的方案了。。
  2. 试试AI给出的方案
    查找最新检查点
    1
    pg_controldata -D /var/lib/postgresql/12/main

    查找最新检查点对应的事务id
    1
    pg_waldump -b -s C/46000400 -p /var/lib/postgresql/12/main

    可惜了这里也报错,并且查询出来的事务id对咱们没用。
  3. 另辟蹊径
    既然数据文件已经损坏了,而且损坏的不是所有文件,说明只要有办法恢复文件的完整性,就可以保住未损坏文件对应的数据。
    那么咱们直接给不存在的文件填充为空。
    1
    dd if=/dev/zero of=/var/lib/postgresql/12/main/base/17506/2703 bs=1M count=1

    此时可以看到,再次执行查询语句的时候,报的是另一个错,那么咱们就按照提示重建索引。
    1
    reindex index pg_cast_source_target_index;

    这时问题又来了,重建索引后查询不会报错,但是没数据呀。所以我又检查了其他的表,结果又让我发现一个文件不存在,果不其然这些数据对象是有其他索引相关联的。

    按照这个思路把所有的表都校验了一遍,现在不报错了,但是只有几个表是有数据的,这不合理呀,缺失的文件不到10个,不能丢这么多数据吧。
    先不管这么多了,已经能正常查询数据了,先把整个库导出一下吧。
    1
    pg_dump -v -d demo_uav -f demo_uav.sql

    你猜怎么着,居然还没完事,那就继续填充空文件和重建索引呗,就这么一直重复操作,最终皇天不负有心人,数据库被我给dump下来了。
    抱着试一试的想法,我再执行了一次SQL,就查刚刚没数据的uav表,结果有数据了。
    于是乎我有开始检查所有表,最后发现只有一张表是丢数据的,好在这个库在2个月前备份过一次,而且这张表也不是经常变动的,一共少了十几条数据,真是万幸。

修复硬盘坏道

前面已经把硬盘数据备份到固态盘了,这会儿可以放心格盘。

1
2
3
4
5
6
7
8
# 移除挂载
umount /mnt/data
# 格式化
mkfs.ext4 /dev/sdb
# 检查坏道并屏蔽
e2fsck -c /dev/sdb -y
# 挂载硬盘
mount -a

恢复数据库

重新创建数据库并把sql文件导入

1
psql -U postgres -d demo_uav -f /mnt/data/demo_uav.sql

重启服务并测试硬盘读写

这里就不再赘述了,到此整个修复过程就结束了,仅此记录,希望对其他人也有帮助。