日志出现 the database system is in recovery mode,同时邻近业务延迟上升时,先不要重启碰运气。把 PostgreSQL 日志、块设备指标和恢复进程的 I/O 对齐到同一时间窗;确认恢复预读已经启用、读 I/O 饱和,而且负载确实影响了同盘业务后,再把 recovery_prefetch=off 作为候选处置。社区关于该参数导致 outage 的讨论只能作为排查线索,不能替代本机证据。
如果恢复阶段连不上 SQL,不要把 SHOW 失败理解成参数不存在,也不要反复停止和启动实例。先从配置管理源或服务器配置中修改实际生效值;reload 是否足够必须按目标版本文档判断,否则安排一次受控重启。recovery_prefetch 的官方定义是恢复期间预取 WAL 所引用的数据块,不是把整个 WAL 文件提前读完。
recovery_prefetch 到底在预读什么?
它尝试在 redo 真正访问数据页之前,把 WAL 指向的数据块提前读入缓存。PITR、standby 回放和崩溃恢复都可能受它影响,但参数只作用于正在执行恢复的实例。它也不决定恢复在哪里停止;停止位置由 recovery_target_time 等 recovery_target_* 参数控制。 PITR 本身仍依赖基础备份和后续 WAL。
三个取值中,on 会尝试预读,预取 I/O 出错时可能使恢复失败;try 仍会尝试预读,但遇到预取 I/O 错误时可以放弃预读并继续恢复;off 不做预读。 因此,try 并不是低 I/O 模式,它与 on 的主要差别是错误处理方式,不代表请求量一定更小。关闭预读也不会取消 redo 所需的数据读取。
一些教程把 on 描述成加速恢复选项,甚至标注为特定版本以后可用。 这类版本范围不能代替目标大版本的官方文档,实际值应通过 SHOW recovery_prefetch 或配置文件确认。
磁盘打满,怎么证明是它而不是别的任务?
先在 Linux 上把日志和设备指标放进同一个观察窗口。%util 高、一次延迟尖峰只能说明设备忙,不能单独证明是预读导致的。iostat 要结合读吞吐、r_await 和队列深度看,pidstat 则用于确认 postgres 进程的读流量是否同步上升。日志过滤可以沿用按时间窗口查找 recovery 记录的方法。
journalctl -u postgresql --since "1 hour ago" \
| grep -Ei 'recovery|redo|consistent|ready to accept'
iostat -xz 1 10
pidstat -d 1 10
服务名不一定都是 postgresql,容器、发行版和集群服务可能使用其他 systemd unit。iostat 和 pidstat 需要系统已有对应的统计工具。
如果实例允许连接,再确认版本、恢复状态、有效值和配置覆盖关系:
SELECT current_setting('server_version_num');
SELECT pg_is_in_recovery();
SHOW recovery_prefetch;
SELECT setting, context, pending_restart
FROM pg_settings
WHERE name = 'recovery_prefetch';
SELECT sourcefile, sourceline, setting, applied, error
FROM pg_file_settings
WHERE name = 'recovery_prefetch';
pg_file_settings 中可能有重复定义,applied 和 error 比单纯搜索文本更重要。无法连接时跳过这些查询,继续检查 postgresql.conf、包含文件、postgresql.auto.conf 以及服务启动参数。
同时也要排查 pg_basebackup、主库正常读写、checkpoint、备份软件和共享存储上的其他实例。pg_basebackup 复制数据文件或 WAL 时同样可能制造很高的读 I/O。
什么情况下该改成 off,什么时候不要动?
只有在恢复预读确实启用、设备读 I/O 已经饱和,而且同盘业务受到实际影响时,才值得优先对照测试 off。本地高速盘、WAL 访问局部性较好、没有旁路业务受影响且恢复时间目标更重要时,不应仅因为热点文章就修改现值。
| 条件 | 需要看到的证据 | 候选动作 | 需要接受的代价 |
|---|---|---|---|
| 共享或较慢存储,同盘业务明显受损 | 预读启用、读等待和队列持续升高 | 对照测试 off |
PITR 或 WAL 回放可能变慢 |
| 本地高速盘,WAL 局部性高 | 没有邻近业务受损,恢复速度更重要 | 保留现值并压测 | 保持当前恢复速度 |
当前为 try 或配置未显式写出 |
先确认有效值和目标版本文档 | 不假设默认值在所有版本一致 | 版本差异可能导致判断错误 |
当前为 on |
相同 WAL、存储和业务条件下比较 | 测试 try 或 off |
结果依赖存储和并发负载 |
已经是 off 但 I/O 仍饱和 |
必要数据页读取仍在继续 | 停止围绕预读调参 | 需要转查其他 I/O 来源 |
关闭预读可能延长 PITR 完成时间,也可能增加 standby 的回放延迟;反过来,共享存储上的额外预读又可能拖慢主库或同盘业务。取舍必须落到本机的 WAL 访问模式和存储竞争上。
恢复中连不上时,配置到底怎么改?
archive recovery 前期和部分 standby 状态可能无法执行常规 SQL,因此不能把 ALTER SYSTEM 当成所有恢复场景都可用的路径。先定位实际生效配置,注意 postgresql.auto.conf、配置文件中的 include 指令以及服务启动参数都可能覆盖前面的设置。
grep -nE '^[[:space:]]*recovery_prefetch[[:space:]]*=' \
"$PGDATA/postgresql.conf" "$PGDATA/postgresql.auto.conf"
grep -nE '^[[:space:]]*include' "$PGDATA/postgresql.conf"
然后在真正管理该参数的位置设置:
recovery_prefetch = off
如果配置由自动化系统生成,应修改它的源配置,不要只改一个下次部署就会被覆盖的生成文件。官方文档只允许通过服务器配置文件或启动参数设置这个参数。
pg_ctl reload -D "$PGDATA"
不要把 pg_ctl reload 成功等同于参数已经生效:只有目标版本官方文档确认该参数支持 SIGHUP reload 时,才能把 reload 当成生效路径;否则应按现有服务管理方式安排受控重启。不要直接复制通用的 stop/start 命令,也不要在尚未确认 WAL、归档和恢复信号状态时连续重启。
如果数据库仍然可连接,并且恢复状态允许修改配置,也可以使用:
ALTER SYSTEM SET recovery_prefetch = 'off';
SELECT pg_reload_conf();
pg_reload_conf() 只说明信号是否发送,不代表运行中的 postmaster 已采用新值。仍需核对 pg_settings.context、pending_restart 和重启后的 SHOW 结果。
改完怎么判断该保留还是撤回?
改动前后使用同一套观察口径:有效参数、WAL 回放位置或 PITR 完成时间、设备读写吞吐与等待、队列深度,以及邻近业务延迟。一次只改变 recovery_prefetch,其他恢复参数、WAL 条件和业务负载尽量保持一致。
standby 可以连接时,可重复采样:
SHOW recovery_prefetch;
SELECT pg_is_in_recovery(),
pg_last_wal_replay_lsn(),
pg_wal_lsn_diff(
pg_current_wal_lsn(),
pg_last_wal_replay_lsn()
) AS replay_lag_bytes;
单次 replay_lag_bytes 不足以判断趋势,应连续观察。PITR 无法连接 SQL 时,则比较恢复日志中的回放进度与最终完成时间。
若改为 off 后磁盘压力和邻近业务延迟下降,而恢复时长或回放延迟仍可接受,就保留该配置。若恢复变慢到不可接受,撤回原值并受控重启;若设备仍然饱和,就停止继续围绕预读调参,转查 redo 的必要数据读取、基础数据恢复、pg_basebackup、备份任务或同存储域的其他消费者。












暂无评论内容