文章目录
-
-
- [一、WAL 文件为何会堆积?](#一、WAL 文件为何会堆积?)
- 二、核心防护策略
-
- [策略 1:确保 `archive_command` 健壮可靠](#策略 1:确保
archive_command健壮可靠) - [策略 2:启用并合理设置 `archive_timeout`](#策略 2:启用并合理设置
archive_timeout) - [策略 3:监控并限制复制槽的 WAL 保留量(PostgreSQL 13+)](#策略 3:监控并限制复制槽的 WAL 保留量(PostgreSQL 13+))
- [策略 4:定期清理失效的复制槽](#策略 4:定期清理失效的复制槽)
- [策略 5:合理配置 `wal_keep_size`(替代旧版 `wal_keep_segments`)](#策略 5:合理配置
wal_keep_size(替代旧版wal_keep_segments)) - [策略 6:使用 `pg_archivecleanup`(仅用于归档目录)](#策略 6:使用
pg_archivecleanup(仅用于归档目录))
- [策略 1:确保 `archive_command` 健壮可靠](#策略 1:确保
- 三、关键监控指标
-
- [1. WAL 目录大小](#1. WAL 目录大小)
- [2. WAL 积压量(通过 LSN 差值)](#2. WAL 积压量(通过 LSN 差值))
- [3. 归档失败次数(需解析日志)](#3. 归档失败次数(需解析日志))
- [4. 使用 `check_postgres` 或 Prometheus Exporter](#4. 使用
check_postgres或 Prometheus Exporter)
- 四、应急处理:磁盘已满怎么办?
- 五、实践建议
- 六、配置案例(生产环境推荐)
-
在 PostgreSQL 中,Write-Ahead Logging(WAL)是保障数据持久性与崩溃恢复的核心机制。然而,在开启 WAL 归档(archive_mode = on)或流复制(replication)的场景下,若未合理配置和管理,WAL 文件可能持续累积,最终导致磁盘空间耗尽,引发数据库服务中断甚至系统崩溃。
本文将系统性地详解 如何防止 WAL 文件撑爆磁盘,涵盖原理、风险识别、核心配置、监控手段及最佳实践,适用于 PostgreSQL 10 及以上版本(包括 12/13/14/15/16)。
一、WAL 文件为何会堆积?
WAL 文件(通常位于 pg_wal 目录,旧版本为 pg_xlog)只有在满足以下条件时才会被自动清理:
- 已完成检查点(checkpoint);
- 该 WAL 段不再被任何以下用途需要 :
- 崩溃恢复(crash recovery)
- 流复制(standby 或 logical replication slot)
- WAL 归档(archive_command 尚未成功执行)
- 逻辑复制槽(logical replication slot)未消费
- 用户手动保留(如 pg_basebackup 运行中)
常见导致 WAL 堆积的场景:
| 场景 | 原因 |
|---|---|
archive_command 失败 |
归档脚本返回非 0 状态,PostgreSQL 认为归档未完成,拒绝删除 WAL |
备库断连且未配置 max_slot_wal_keep_size |
主库为备库保留所有 WAL,直到备库重新连接 |
| 逻辑复制槽停滞(slot inactive) | 消费者长时间不拉取 WAL,主库无限保留 |
| 手动备份未完成 | 如 pg_basebackup 被中断,但未清理临时状态 |
| 磁盘 I/O 性能差 | checkpoint 无法及时推进,WAL 释放滞后 |
二、核心防护策略
策略 1:确保 archive_command 健壮可靠
这是最常见问题源头。必须保证归档命令幂等、容错、快速失败。
错误示例:
conf
archive_command = 'cp %p /archive/%f'
- 若
/archive满或权限不足,cp失败 → WAL 永久堆积。
正确做法:
conf
archive_command = 'test ! -f /archive/%f && cp %p /archive/%f'
或使用带超时和日志的脚本:
bash
#!/bin/bash
# /usr/local/bin/archive_wal.sh
set -e
WAL_FILE="$1"
DEST="/archive/$WAL_FILE"
# 防止重复归档
if [ -f "$DEST" ]; then
exit 0
fi
# 限制单次归档时间(避免 hang 住)
timeout 30 cp "$PGDATA/pg_wal/$WAL_FILE" "$DEST" || {
logger "ARCHIVE FAILED: $WAL_FILE"
exit 1
}
logger "ARCHIVE SUCCESS: $WAL_FILE"
exit 0
conf
archive_command = '/usr/local/bin/archive_wal.sh %f'
关键:任何情况下,失败必须快速退出(exit 1),成功必须 exit 0。
策略 2:启用并合理设置 archive_timeout
强制定期切换 WAL 段,避免长时间无写入导致归档停滞。
conf
archive_timeout = 300 # 每 5 分钟强制切换 WAL(即使无事务)
适用于低负载系统,确保归档持续进行。
策略 3:监控并限制复制槽的 WAL 保留量(PostgreSQL 13+)
从 v13 起,可设置全局上限:
conf
max_slot_wal_keep_size = 2GB
- 当所有复制槽所需的 WAL 总量超过此值,PostgreSQL 会自动丢弃最旧的 WAL ,并标记对应 slot 为
invalid。 - 避免因一个停滞的逻辑复制消费者导致整个集群磁盘爆满。
注意:v12 及以下无此参数,需手动监控和清理。
策略 4:定期清理失效的复制槽
查询停滞的 slot:
sql
SELECT slot_name, slot_type, active, restart_lsn,
pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) AS retained_bytes
FROM pg_replication_slots;
若 active = false 且 retained_bytes 持续增长,应删除:
sql
SELECT pg_drop_replication_slot('stale_slot_name');
建议通过监控告警自动处理。
策略 5:合理配置 wal_keep_size(替代旧版 wal_keep_segments)
控制主库为备库保留的 WAL 量(不依赖 slot):
conf
wal_keep_size = 1GB # 保留至少 1GB 的 WAL 供备库追赶
- 备库断连后,最多可落后 1GB WAL;
- 超出后,备库需重建(re-init)。
避免设为过大(如 100GB),否则仍可能撑爆磁盘。
策略 6:使用 pg_archivecleanup(仅用于归档目录)
若使用基于归档的 PITR(而非流复制),可在备库或归档服务器上定期清理旧 WAL:
bash
# 保留最近 7 天的 WAL
find /archive -name "*.wal" -mtime +7 -delete
或使用 PostgreSQL 自带工具(需指定最新需保留的 WAL):
bash
pg_archivecleanup /archive 000000010000000A000000B0
注意:
pg_archivecleanup不能用于主库的 pg_wal 目录!
三、关键监控指标
1. WAL 目录大小
bash
du -sh $PGDATA/pg_wal
2. WAL 积压量(通过 LSN 差值)
sql
-- 主库:查看最滞后的 slot
SELECT slot_name,
pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) AS bytes_behind
FROM pg_replication_slots
ORDER BY bytes_behind DESC;
3. 归档失败次数(需解析日志)
在 postgresql.conf 中启用:
conf
log_checkpoints = on
log_statement = 'none'
log_min_messages = warning
搜索日志中的:
LOG: archive command failed
4. 使用 check_postgres 或 Prometheus Exporter
check_postgres.pl --action=wal_filespostgres_exporter暴露pg_wal_writes、pg_replication_slots等指标
四、应急处理:磁盘已满怎么办?
-
立即扩容或清理其他文件(临时缓解);
-
暂停非关键写入,减少新 WAL 生成;
-
强制推进归档 :
sqlSELECT pg_switch_wal(); -- 强制切换当前 WAL 段,促使其进入归档队列 -
若归档失败,手动修复 archive_command 并重试;
-
删除无效复制槽(如确认不再需要);
-
极端情况 :临时关闭
archive_mode(需重启),但会丢失 PITR 能力,慎用!
五、实践建议
| 措施 | 说明 |
|---|---|
| 健壮的 archive_command | 必须处理失败、幂等、带超时 |
| 设置 max_slot_wal_keep_size(v13+) | 防止单个 slot 拖垮整个系统 |
| 监控复制槽活跃状态 | 自动告警并清理失效 slot |
| 合理配置 wal_keep_size | 避免过大保留 |
| 启用 archive_timeout | 保证低负载系统也能归档 |
| 定期演练 PITR 恢复 | 验证归档链完整性 |
| WAL 目录独立挂载 | 避免撑爆系统盘,便于扩容 |
六、配置案例(生产环境推荐)
conf
# WAL 基础
wal_level = replica
max_wal_size = 4GB
min_wal_size = 1GB
checkpoint_timeout = 15min
checkpoint_completion_target = 0.9
# 归档
archive_mode = on
archive_command = '/usr/local/bin/archive_wal.sh %f'
archive_timeout = 300
# 复制控制(v13+)
max_slot_wal_keep_size = 8GB
wal_keep_size = 2GB
# 日志
log_checkpoints = on
log_min_messages = warning
总结:WAL 管理是 PostgreSQL 高可用与数据安全的基石,但也是运维中最易忽视的风险点。"能写入"不等于"能归档",必须从架构设计、配置、监控到应急响应形成闭环。通过上述策略,可有效避免因 WAL 堆积导致的灾难性故障,保障数据库稳定运行。