当 PostgreSQL 的数据校验和发现页面损坏时,标准答案通常是从备份恢复,或者切换到仍然健康的副本。但现实中可能遇到更棘手的情况:备份不可用,备用节点也无法接管,而业务又急需读取部分数据。
这正是 ignore_checksum_failure 存在的场景。它可以让 PostgreSQL 在发现校验和错误后继续处理读取请求,但代价是:数据库可能返回已经损坏的数据,甚至在后续操作中放大问题。它是应急读取开关,不是修复工具。
数据校验和保护了什么
PostgreSQL 的数据校验和用于检测数据页在写入后是否发生了变化。常见原因包括存储介质故障、内存或控制器问题、文件系统异常,以及底层硬件静默损坏。
启用校验和后,读取页面时 PostgreSQL 会验证页面内容。如果校验失败,默认行为是拒绝继续读取,并报告类似下面的错误:
ERROR: invalid page in block 123 of relation base/16384/24576
DETAIL: Page verification failed, calculated checksum ... but expected ...
具体错误文本会随 PostgreSQL 版本和损坏位置变化。重要的是,这类错误意味着页面内容不再可信,不能简单地当作普通查询失败处理。
可以先确认集群是否启用了校验和:
pg_checksums --check --pgdata=/var/lib/postgresql/16/main
pg_checksums 通常要求数据库实例处于停止状态。生产环境执行前应确认版本、数据目录和维护窗口;不要把它当成可以随时在线运行的无风险检查命令。
ignore_checksum_failure 做了什么
将该参数设为 on 后,PostgreSQL 会忽略数据页校验和失败,尝试继续执行访问该页面的操作。它的价值主要是帮助管理员在极端故障期间完成有限的数据提取,例如:
- 从仍可读取的表中导出尽可能多的数据;
- 判断损坏影响了哪些业务对象;
- 在没有可用备份或副本时,抢救部分关键记录;
- 为后续恢复工作收集诊断信息。
它不会重新生成正确的数据,也不会修复磁盘上的页面。更危险的是,查询结果可能包含损坏内容,或者在读取损坏索引、表页时表现出不稳定行为。因此,开启后拿到的数据必须经过业务校验,不能直接视为可靠备份。
一个受控的应急读取示例
下面的例子只在当前会话中开启参数,并把查询结果导出到新文件。执行前请先改成实际连接信息和查询条件。示例假设管理员已经确认没有可用的正常备份或故障转移目标。
export PGHOST=/var/run/postgresql
export PGPORT=5432
export PGDATABASE=appdb
export PGUSER=postgres
psql -X -v ON_ERROR_STOP=1 <<'SQL'
SET statement_timeout = '5min';
SET lock_timeout = '10s';
SET ignore_checksum_failure = on;
\copy (
SELECT id, account_id, created_at, amount
FROM payments
WHERE created_at >= timestamp '2025-01-01'
AND created_at < timestamp '2025-02-01'
ORDER BY id
) TO '/tmp/payments-rescue.csv' WITH (FORMAT csv, HEADER true)
SQL
这个操作有几个边界:
SET只影响当前连接,避免把危险设置永久写入整个集群。ON_ERROR_STOP=1能让脚本在 SQL 错误时停止,但它不能保证导出的数据没有损坏。- 导出文件应放在独立的可靠存储上,并记录执行时间、查询条件和数据库版本。
- 如果查询触碰到损坏页面,仍可能失败;开启参数不等于所有损坏都能绕过。
也可以用连接参数把设置限制在一次命令中:
PGOPTIONS='-c ignore_checksum_failure=on' \
pg_dump --data-only --format=custom --file=/safe-store/appdb-rescue.dump appdb
这更适合尝试抢救可读数据,但不能替代正常备份。导出完成后,应在隔离的临时实例中恢复并做行数、主键、金额、时间范围等业务级校验。
使用时最容易犯的错误
把它当成修复手段
ignore_checksum_failure 只改变读取错误的处理方式,不会修复数据页。真正的恢复路径仍然是:使用经过验证的备份恢复,或从健康副本重建实例,并调查底层存储和硬件问题。
在主配置中长期打开
把参数写入 postgresql.conf 或通过 ALTER SYSTEM 持久化,会让后续所有连接都处于更宽松的校验模式,扩大错误数据被读取和传播的范围。应急读取完成后,至少确认没有会话继续使用该设置;如果曾经持久化配置,应立即恢复为 off 并重新加载配置。
ALTER SYSTEM SET ignore_checksum_failure = off;
SELECT pg_reload_conf();
SELECT name, setting, source, pending_restart
FROM pg_settings
WHERE name = 'ignore_checksum_failure';
如果参数只通过当前会话设置,则断开连接即可失效。但仍要检查应用连接池,因为连接池可能复用已经设置过该参数的后端连接。
只看数据库日志,不查存储层
校验和失败通常是存储链路问题的信号。恢复数据后,还需要检查磁盘健康状态、文件系统日志、控制器日志、虚拟化平台事件,以及主机内存和电源稳定性。否则即使重新恢复数据库,损坏也可能再次出现。
更稳妥的决策顺序
可以按下面的顺序处理:
- 立即限制写入,避免继续修改可能相关的数据。
- 保存错误日志、实例状态和故障发生时间。
- 尝试从健康副本或经过验证的备份恢复。
- 如果没有可用恢复源,建立隔离的抢救实例或只读连接。
- 仅对明确的表和时间范围开启
ignore_checksum_failure,分批导出数据。 - 对导出结果执行业务校验,并保留原始文件和操作记录。
- 重建数据库、检查底层硬件,并重新验证备份和复制链路。
结语:它是最后一道读取手段
ignore_checksum_failure 的合理定位是“没有正常恢复路径时,尽量多读出一些东西”。它适合短时间、受控范围、可审计的抢救操作,不适合日常运行,更不适合用来掩盖持续的存储故障。
如果团队确实需要依赖这个参数,应提前准备隔离实例、可靠的导出存储、业务校验脚本和硬件排查流程。真正可靠的方案仍然是可验证的备份、可用的故障转移副本,以及经过演练的恢复流程。