PostgreSQL在开启归档模式后,会将生成的WAL日志段持续复制到归档目录,供后续做基础备份加归档恢复使用。正常情况下,归档文件会被备份系统拉走或者定期清理,但如果归档命令执行失败、备份流程中断,归档目录就会不断膨胀,最终把磁盘写满,直接导致数据库写入阻塞。这篇文章从排查思路、紧急处理和长期预防三个方面,完整讲清楚归档目录空间告警的应对方法。

一、为什么归档目录会持续膨胀
归档目录膨胀的直接原因只有一个:新产生的归档文件速度大于文件被清理或转移的速度。但背后的诱因多种多样,最常见的有以下几类。
第一类是archive_command执行失败。PostgreSQL的归档机制是每次切换WAL段后,由archiver进程执行archive_command参数指定的命令,只有该命令返回0才认为归档成功。如果目标目录不可写、远程主机网络不通、存储设备故障,命令返回非0值,PostgreSQL会不断重试,同时新的WAL段继续产生,pg_wal目录和归档目录双双堆积。第二类是备份系统没有及时清理已归档文件,比如pgBackRest、WAL-G等备份工具的保留策略配置过宽,或者清理定时任务被误删。第三类是主备架构下延迟问题,流复制备库还在使用旧的WAL,主库的wal_keep_size设置过大,连带归档量增加。
还有一种容易被忽视的情况:archive_command配置的命令本身有语法错误或者权限不足。比如命令中调用的脚本没有可执行权限,归档从库一开始就失败。这类问题在库刚上线时不易发现,往往运行几周后磁盘告警才暴露出来。
二、如何快速定位归档堆积的原因
排查的第一步是查看归档统计视图pg_stat_archiver,它记录了归档的成功次数、失败次数、最近一次失败的WAL文件名和时间。通过下面的SQL可以直接了解归档状态:
-- 查看归档统计信息
SELECT archived_count,
last_archived_wal,
last_archived_time,
failed_count,
last_failed_wal,
last_failed_time
FROM pg_stat_archiver;
如果failed_count很高,且last_failed_time就是最近时间点,说明归档命令一直在失败。接着查看数据库日志,archiver进程的报错信息会记录在log目录中,常见的错误包括permission denied、No space left on device、connection timed out等,根据具体报错逐项处理即可。
第二步是检查pg_wal目录本身的文件数量。pg_wal目录堆积和归档目录堆积经常同时发生,因为归档失败的文件不会被移走。执行下面两条命令可以快速判断:
# 查看归档目录和pg_wal目录的空间占用 du -sh /pgarchive/* | sort -rh | head -20 ls /pgdata/pg_wal | wc -l # 查看归档相关参数配置 psql -c "SHOW archive_mode; SHOW archive_command;"
第三步是确认清理机制是否还在工作。如果使用了pg_backrest或WAL-G,检查其保留策略参数,例如pg_backrest的repo-retention-full设置。如果没有用备份工具,而是靠cron任务定期删除,检查crontab中任务是否存在、脚本日志有无报错。
三、紧急处理:恢复数据库写入
当磁盘被归档文件写满,数据库的写入会直接报错could not write to file pg_wal,业务受到影响。此时第一优先级是把空间释放出来,让数据库恢复运转。可以先手动删除一部分已经归档成功且备份系统已确认拉走的文件,腾出应急空间。
删除前务必确认文件状态。只删除那些满足以下条件的文件:在pg_wal中已经不存在同名文件、备份系统已经完整接收、基础备份能够覆盖的时间范围。可以用下面的脚本谨慎清理:
#!/bin/bash # 清理已被备份系统接收且超过7天的归档文件 ARCHIVE_DIR=/pgarchive find $ARCHIVE_DIR -name "*.backup" -mtime +7 -delete find $ARCHIVE_DIR -name "0000000[1-9]*" -mtime +7 -delete
注意,绝对不要删除pg_wal目录下的任何文件,那个目录由PostgreSQL自行管理,手动删除会导致数据库损坏甚至无法启动。如果pg_wal目录也因为归档失败而堆积,正确的做法是修复archive_command让它恢复工作,archiver进程会自动补归档并释放pg_wal的空间。
如果归档目标存储短期内无法恢复,可以考虑临时把archive_mode改为off让数据库先跑起来。但这个操作需要重启数据库,且期间的WAL不会被归档,意味着这段时间的数据无法通过基础备份加归档恢复,需要谨慎评估风险。修改方法如下:
-- postgresql.conf 中修改 -- archive_mode = off -- archive_command = '' -- 修改后需要重启数据库生效 SELECT pg_reload_conf(); -- archive_mode 是postmaster级别参数,必须重启
四、建立监控与自动清理的长效机制
事后补救不如事前监控。归档目录的空间监控应该纳入数据库监控体系,可以用Prometheus加postgres_exporter,或者简单的shell脚本配合zabbix。下面是一个简单的监控脚本,当空间使用率超过80%时告警:
#!/bin/bash
# 归档目录空间监控脚本
ARCHIVE_DIR=/pgarchive
USAGE=$(df -h $ARCHIVE_DIR | awk 'NR==2 {gsub(/%/,""); print $5}')
if [ $USAGE -gt 80 ]; then
echo "告警:归档目录使用率 ${USAGE}%,超过阈值80%"
# 调用告警接口发送通知
fi
除了空间监控,还应该监控归档延迟。last_archived_time与当前时间的差值如果持续增大,说明归档速度跟不上WAL生成速度,即使空间暂时正常也要及时介入。pg_stat_archiver视图配合定时SQL采集就能实现:
-- 检查归档延迟,超过10分钟告警 SELECT EXTRACT(EPOCH FROM (now() - last_archived_time)) AS delay_seconds FROM pg_stat_archiver WHERE last_archived_time IS NOT NULL;
自动清理方面,如果使用pgBackRest,配置repo-retention-full和repo-retention-archive两个参数即可自动管理归档文件的保留周期。自建方案则建议编写清理脚本,逻辑是:先调用pg_backup_start做基础备份,再删除早于该基础备份最早WAL之前的归档文件,保证任意时刻都存在可恢复的备份链。清理任务上线前要在测试环境充分验证,避免误删导致备份断链。
最后提醒一点,archive_command建议配置成幂等且带日志的形式,例如复制命令加上校验比对,失败时把错误写入独立日志文件,这样故障发生时能第一时间拿到线索,而不是只看到归档目录里一堆待处理的文件。把监控、告警、自动清理三件事做扎实,归档空间告警基本可以从根源上避免。
PostgreSQL归档归档目录空间告警pg_wal修改时间:2026-09-06 18:36:38