数据库因坏块导致无法VACUUM FREEZE问题处理

文章目录

  • 环境
  • 症状
  • 问题原因
  • 解决方案

环境

系统平台:N/A
版本:4.5.8,4.5.7,4.5.6,6.0

症状

数据库因事务年龄达到21亿,导致事务冻结,无法进行增删改。 数据库日志中提示:

<2025-05-13 13:55:41.693 CST app:[未知] db:db_1 pid:2869609 state:54000 tran:0 sessiontime:2025-05-13 13:55:21 CST>语句: INSERT INTO table xxx values xxx <2025-05-13 13:55:41.693 CST app:[未知] db:db_1 pid:2869611 state:54000 tran:0 sessiontime:2025-05-13 13:55:21 CST>错误: 数据库没有接收命令来避免在数据库"db_1"中的重叠数据损失 <2025-05-13 13:55:41.693 CST app:[未知] db:db_1 pid:2869611 state:54000 tran:0 sessiontime:2025-05-13 13:55:21 CST>提示: 停止postmaster进程,然后在单用户模式下清理数据库. 您也可能需要提交或回滚旧的已准备好事务,或者旧的复制槽. 。。。 <2025-05-13 13:57:26.035 CST app: db: pid:2870085 state:XX001 tran:0 sessiontime:2025-05-13 13:55:23 CST>错误: 无法读取文件"base/16387/220001"的块0:只读取了8192字节的0 <2025-05-13 13:57:26.035 CST app: db: pid:2870085 state:XX001 tran:0 sessiontime:2025-05-13 13:55:23 CST>上下文: 对表"db_1.public.table_namexxx"进行自动清理

问题原因

数据文件所在的磁盘或文件系统可能出现物理损坏或存储错误,导致块损坏,数据库无法对相关表进行事务年龄回收。

解决方案

  1. 检查坏块对应的数据库对象
    以数据库文件base/16387/220001 为例:
select*frompg_databasewhereoid='16387';

数据库名称为db_1,则切换到db_1查询对应对象名称、类型等信息。

selectoid,relname,relnamespace,relkindfrompg_classwhererelfilenode=220001;
  1. 坏块对象处理

2.1 假如对象为索引:
寻找合适时间,删除和重建索引。

2.2 假如对象为表
寻找合适时间,配置zero_damaged_pages=on,使用pg_dump导出数据,重新建表后导入。 注意:修改该参数前,强烈进行数据库的完整物理备份。

altersystemsetzero_damaged_pages=on;selectpg_reload_conf();

关于zero_damaged_pages参数介绍,请参考:

059921204 PostgreSQL坏块忽略参数的介绍
假如坏块较多,则建议重建实例后,进行全库导入,注意排查硬盘和文件系统是否存在IO错误。

  1. 再次进行事务ID回收

完成表重建或索引重建后,需要手动进入单用户模式,执行

VACUUM FREEZE;

单用户模式,请参考:

瀚高技术支持平台:012067504 瀚高数据库的单用户模式介绍 postgres --single