1. 基本情况
在一次对某业务系统的PostgreSQL 12.4数据库进行常规维护后,监控系统发出告警,显示主库的WAL(预写式日志)归档目录 /archive 磁盘使用率持续快速增长,远超日常水平。经初步查看,发现该目录下积压了大量已生成的*.ready标记文件,但对应的WAL日志文件(如0000000100001234000000A9)并未被归档脚本成功转移至远程备份存储。
数据库本身运行正常,业务未受影响。但此情况若持续,将导致归档目录所在磁盘被写满,进而可能触发数据库因无法继续归档而挂起(如果archive_command失败且archive_mode为on),对系统高可用性和数据安全性构成严重威胁。此问题即典型的“孤儿”归档问题——WAL日志文件已生成并标记为就绪,但归档流程未完成或失败,留下“孤儿”状态的*.ready文件。
2. 问题定位
我们立即组建了处理小组,按照以下步骤进行问题定位
(1)检查归档状态与配置:
执行 SELECT * FROM pg_stat_archiver;。关键发现:last_failed_wal和last_failed_time字段有持续记录,且failed_count在增长。last_archived_wal的LSN位置远落后于last_archived_time,表明归档确实长时间未成功。
检查postgresql.conf中相关配置:
ini archive\_mode = on archive\_command = 'cp %p /archive/%f && rm /archive/%f\*.ready && touch /archive/%f.done'
配置本身无语法错误。归档目录权限也经检查,属主为postgres用户,权限正常。
(2)检查归档脚本与执行环境:
手动执行archive_command中的命令,模拟归档过程。发现当WAL文件较大或网络存储(NFS)瞬时延迟时,cp命令偶发性超时或返回错误,但数据库后台归档进程(archiver)仅记录了失败,并未重试或清理*.ready文件。* 检查归档目标存储(NFS挂载点)的可用空间和I/O状态,发现其在问题发生时间段存在间歇性高延迟。
(3)诊断核心问题:
根本原因:归档命令(cp)因外部存储(NFS)不稳定而间歇性失败,PostgreSQL的archiver进程在命令返回非零退出码时,认为该次归档失败。关键点在于:它不会自动删除已生成的*.ready标记文件。当下一个WAL段文件产生并再次标记为*.ready时,之前失败的*.ready文件就变成了“孤儿”,持续累积,导致归档目录被大量*.ready文件填满。
触发条件:网络存储的短暂性能波动,结合了过于简单的归档命令(缺乏重试和健壮的错误处理)。
3. 处理过程
处理过程遵循“先恢复,后根治”的原则,确保业务连续性的前提下解决问题。
阶段一:紧急恢复(清除非活动WAL文件)
(1)评估风险
确认积压的WAL文件对应的LSN位置(/archive目录下最旧的*.ready文件)仍晚于所有备库和基础备份所需的最旧LSN(可通过pg_controldata和备库复制状态确认)。确保清理这些文件不会影响未来的PITR(时间点恢复)。
(2)安全清理:
停止或暂停归档进程(可选):pg_ctl pause 或 临时将archive_command改为’/bin/true’并重载配置。但考虑到业务压力,我们选择了更动态的方式。* 编写并执行安全清理脚本。绝对禁止直接rm /archive/**.ready。
bash
#!/bin/bash
ARCHIVE\_DIR=/archive
\# 找出所有未对应WAL文件的\*.ready文件(即“孤儿”文件)
for ready\_file in $ARCHIVE\_DIR/\*\*.ready; do
wal\_file=${ready\_file%\*.ready}
if \[ ! -f $wal\_file \]; then
echo "Removing orphaned ready file: $ready\_file"
rm $ready\_file
fi
done
执行后,归档目录磁盘使用率立即下降。
阶段二:修复归档流程
(1)增强archive_command:将原先脆弱的cp命令替换为具有重试机制和更完善错误处理的脚本。
ini
archive_command = ‘/usr/local/bin/robust_archive.sh %p %f’
robust_archive.sh
脚本内容核心:
bash
#!/bin/bash
src=$1
dest\_name=$2
ARCHIVE\_DIR=/archive
REMOTE\_LOCATION=backup-server:/pg\_archives/
MAX\_RETRIES=3
RETRY\_DELAY=5
for i in $(seq 1 $MAX\_RETRIES); do
使用rsync替代cp,支持断点续传和更多错误检查
if rsync -a $src $REMOTE\_LOCATION$dest\_name; then
# 成功归档后,清理本地\*.ready文件,创建.done标记(可选)
rm -f $ARCHIVE\_DIR/$dest\_name\*.ready
touch $ARCHIVE\_DIR/$dest\_name.done
exit 0
fi
echo "Archive attempt $i failed for $dest\_name"
sleep $RETRY\_DELAY
done
所有重试失败后,记录严重错误,但返回失败让数据库知晓
logger -p local0.error “FATAL: Failed to archive WAL file $dest_name after $MAX_RETRIES attempts.”
exit 1
(2)配置调整与测试:
- 将脚本部署到所有数据库服务器,设置正确的权限。
- 临时创建一个测试WAL文件,手动执行新归档脚本,验证其功能。
- 重载PostgreSQL配置:SELECT pg_reload_conf();。
- 观察pg_stat_archiver视图,failed_count停止增长,last_archived_wal开始追赶当前WAL位置。
阶段三:监控与验证
(1)增强监控:在监控平台(如Zabbix、Prometheus)中添加对pg_stat_archiver中failed_count、last_archived_wal滞后度以及归档目录中*.ready文件数量的监控和告警。
(2)长期观察:连续观察24小时,确认归档流程稳定,无新的“孤儿”文件产生,归档延迟恢复正常水平。
4、经验总结
(1)归档命令的健壮性是生命线:切勿使用简单的cp或scp作为生产环境的archive_command。必须包含重试机制、详细的错误日志,并考虑使用如rsync等更可靠的传输工具。命令的返回值必须准确反映成功(0)或失败(非0)。
(2)“孤儿”*.ready文件是典型症状:归档目录磁盘爆满或*.ready文件堆积,首要怀疑归档命令本身或其所依赖的环境(网络、存储、权限)出现问题。pg_stat_archiver是诊断的第一站。
(3)清理操作务必谨慎:在清理任何WAL相关文件前,必须双重甚至三重确认这些文件不再被数据库实例、任何运行的备库或未来的恢复计划所需要。误删可能导致数据永久丢失或无法恢复。编写脚本进行有选择的清理,而非盲目删除。
(4)监控必须覆盖归档链路:除了数据库状态,还需监控归档目标存储的可用空间、I/O性能、网络连通性。对归档失败次数和归档延迟设置主动告警,实现“早发现,早处理”。5. 建立标准化与预案:将经过验证的、健壮的归档脚本和配置纳入数据库部署标准。针对此类问题,编写标准的应急处理预案(SOP),包括诊断步骤、安全清理方法和回滚方案,以便团队任何成员都能快速响应。
通过本次事件,我们不仅解决了一个具体的技术故障,更完善了数据库运维体系中关于日志归档的配置规范、监控维度和应急流程,显著提升了系统的稳定性和可维护性。




