PostgreSQL 备库回放 WAL 时,日志记录可能指向一个并不存在的页面。正常情况下,数据库会把这视为无法继续保证一致性的严重错误,并以 PANIC 中止恢复。ignore_invalid_pages 能把这类错误降为 WARNING,让恢复继续推进,但它不是常规的高可用开关,而是一次带有数据损坏风险的应急脱困手段。
为什么默认行为必须是 PANIC
WAL 回放的前提是:日志描述的变更能够应用到预期的数据页。如果 WAL 要修改某个块,而对应关系文件、分段文件或页面根本不存在,系统就无法证明后续状态仍然正确。继续回放可能留下几类问题:
- 某些事务的逻辑效果只应用了一部分;
- 索引与堆表失去一致性;
- 页面缺失被暂时掩盖,直到查询、VACUUM 或下一次故障切换才暴露;
- 备库虽然启动成功,却不再是主库的可信副本。
因此,PANIC 不是过度保守,而是在阻止一个不可验证的数据状态继续扩散。ignore_invalid_pages = on 所做的事情,是在恢复期间遇到无效页面引用时记录警告并跳过相关操作。它不能补回页面,也不会自动修复对象。
什么时候才值得考虑开启
这个参数适合非常狭窄的场景:备库已经无法启动,重新创建备库暂时不可行,而团队需要尽快让实例越过故障点,以便导出仍可读取的数据、定位受影响对象,或完成临时取证。
不要把它当成以下问题的长期解决方案:
- 备库反复出现存储 I/O 错误;
- 主备之间频繁缺少 WAL;
- 数据目录被手工删除或被不完整快照覆盖;
- 已经可以通过新的基础备份快速重建备库。
只要能够从健康主库重新初始化备库,重建通常更可靠。开启该参数后成功启动,也不等于节点重新获得了参与故障切换的资格。
一套可审计的应急操作
下面示例假设 PostgreSQL 由 pg_ctl 管理,并且你正在处理备库的副本或快照。运行前把 PGDATA 和日志路径改成实际值。不要先在唯一的数据目录上试验。
#!/usr/bin/env bash
set -euo pipefail
export PGDATA=/var/lib/postgresql/16/main
export RECOVERY_LOG=/tmp/standby-recovery.log
# 保留配置证据,并确认实例已经停止。
pg_ctl -D "$PGDATA" status && pg_ctl -D "$PGDATA" stop -m fast || true
cp "$PGDATA/postgresql.conf" "$PGDATA/postgresql.conf.before-ignore-invalid-pages"
# 追加到文件末尾,使该值覆盖前面可能存在的同名配置。
printf '\n# Temporary recovery override; remove after data extraction\nignore_invalid_pages = on\n' \
>> "$PGDATA/postgresql.conf"
pg_ctl -D "$PGDATA" -l "$RECOVERY_LOG" start
# 搜集所有相关 WARNING;这些日志是后续判断受影响对象的重要线索。
grep -Ei 'invalid page|ignore_invalid_pages|warning|panic' "$RECOVERY_LOG" || true
如果节点能够启动,先验证它仍处于恢复状态,并记录当前 WAL 位置:
psql -X -v ON_ERROR_STOP=1 -d postgres <<'SQL'
SELECT pg_is_in_recovery() AS is_standby,
pg_last_wal_replay_lsn() AS replay_lsn,
pg_last_xact_replay_timestamp() AS replay_time;
SHOW ignore_invalid_pages;
SQL
接下来应把实例隔离在只读、不可提升的环境中,结合警告里的 relation、fork 和 block 信息定位对象。对于仍可读取的关键表,可以导出数据;对于索引问题,可以在可信数据源上重建索引。不要仅仅因为健康检查返回成功,就把该节点重新加入自动故障切换。
完成取证或导出后,应删除临时配置并重启:
export PGDATA=/var/lib/postgresql/16/main
mv "$PGDATA/postgresql.conf.before-ignore-invalid-pages" \
"$PGDATA/postgresql.conf"
pg_ctl -D "$PGDATA" restart
如果恢复在关闭参数后再次失败,这是预期信号:原始不一致仍然存在,正确收尾方式通常是从健康来源重新建立备库。
启用之前的决策清单
在修改配置前,至少回答这些问题:
- 是否已保存数据目录快照、故障日志和原始配置?
- 是否确认问题发生在备库,而不是当前唯一可写主库?
- 是否评估过从主库重新做基础备份所需的时间?
- 是否已经禁止该节点被自动提升?
- 是否有人负责检查所有
WARNING并验证受影响对象? - 是否安排了最终重建,而不是让临时参数永久留在配置中?
ignore_invalid_pages 的价值不在于“修好”备库,而在于给管理员一个越过恢复阻塞点的最后机会。正确的使用方式是:先保全证据,在隔离副本上短暂开启,提取需要的数据,然后重建一个能够重新证明一致性的备库。