排查高 WAL 活动并管理归档日志磁盘空间
学习排查和管理 PostgreSQL 中过多的预写日志 (WAL) 生成。本指南涵盖了高 WAL 活动的常见原因,例如批量操作和复制问题,并提供了配置 WAL 归档、管理复制槽以及防止磁盘空间耗尽的实用解决方案。对于注重稳定性和高效磁盘空间利用的 PostgreSQL 管理员来说,这是必读内容。
排查高 WAL 活动并管理归档日志磁盘空间
PostgreSQL 中高预写日志 (WAL) 活动本身并不一定是问题。繁忙的数据库理应生成 WAL。问题在于当 WAL 生成速率出乎意料、归档的 WAL 从未被清理,或者 pg_wal 因某些原因阻止 PostgreSQL 回收旧段而增长时。
让 WAL 事件变得更糟的最快方法是手动删除 pg_wal 中的文件。不要这样做。将 WAL 磁盘满视为恢复情况:识别是什么保留了 WAL,在安全的情况下创造喘息空间,然后修复导致增长的失败归档、滞后备用节点或废弃槽。
理解预写日志 (WAL)
PostgreSQL 在安全写入相关数据页之前,将更改记录写入 WAL。崩溃后,PostgreSQL 重放 WAL,以便已提交的更改不会丢失。同一流也用于流复制和时间点恢复。
WAL 文件存储在固定大小的段文件中。许多安装使用 16 MB 段,因为这是常见的默认值,但大小是在集群初始化时选择的。写入密集型工作负载可以快速创建大量段。只有在 PostgreSQL 不再需要旧段用于崩溃恢复、检查点、归档、复制或槽时,才会回收或删除它们。
关键 WAL 概念:
- 持久性: 已提交的事务可以在崩溃后恢复。
- 复制: 备用节点从主节点接收 WAL 记录。
- 时间点恢复 (PITR): 基础备份加上归档的 WAL 允许您恢复到保留窗口内的选定时间点。
- WAL 段: WAL 存储在
pg_wal下的段文件中。
高 WAL 活动的常见原因
多个因素可能导致异常高的 WAL 生成量。识别根本原因是有效排查的第一步。
1. 批量数据加载和修改
像 INSERT、UPDATE、DELETE、TRUNCATE 和 COPY 这样的操作可以生成大量 WAL。批量操作,尤其是在大表上,自然会产生比单个小事务更多的 WAL 记录。
- 示例: 一个
COPY FROM命令插入数百万行可能会生成数 GB 的 WAL 数据。 - 示例: 运行大规模数据迁移或批量更新脚本。
2. 复制延迟和备用节点问题
如果您的备用服务器跟不上主节点(复制延迟),WAL 文件将在主节点上累积。主服务器无法移除已完成的 WAL 段,直到它们被确认已发送到所有连接的备用节点并由其处理(如果未配置 wal_keep_size 或 max_slot_wal_keep_size,或者未正确使用槽)。
- 场景: 备用服务器宕机、断开连接或遇到性能问题,导致无法从主节点消费 WAL 记录。
3. 检查点后的全页写入
检查点后,当启用 full_page_writes 时,对数据页的第一次更改可能会记录全页映像。该设置保护恢复免受 torn 页的影响,通常保持开启。如果检查点过于频繁,全页映像会显著增加 WAL 量。解决方法通常是调整检查点行为,而不是禁用持久性保护。
4. 未管理的 pg_wal 目录增长
如果启用了 WAL 归档但失败,PostgreSQL 会保留仍需归档的 WAL 段。如果未启用归档,pg_wal 仍应在不再需要时回收旧段,除非复制、槽或检查点压力保留了它们。
5. 未回收的复制槽
复制槽保证在特定备用节点或逻辑解码客户端消费之前,WAL 段不会被移除。如果创建了槽但消费者停止或断开连接而未删除槽,则该槽所需的 WAL 段将被保留,即使备用节点不再活动。
管理 WAL 磁盘空间:配置和解决方案
解决高 WAL 活动需要多管齐下的方法,包括监控、配置调整和适当的维护程序。
1. 启用并监控 WAL 归档
WAL 归档是管理磁盘空间和启用 PITR 的最关键机制。启用归档后,完成的 WAL 文件会被复制到单独的位置(例如,网络文件共享、S3 存储桶或不同的磁盘)。
配置:
修改您的 postgresql.conf 文件:
wal_level = replica # 对于逻辑复制使用 logical
archive_mode = on # 启用归档
archive_command = 'cp %p /path/to/archive/%f'
# 使用 wal-g 或类似工具上传到 S3 的示例:
# archive_command = 'wal-g wal-push %p'
%p:要归档的 WAL 文件完整路径的占位符。%f:WAL 文件名的占位符。
重要: archive_command 必须能够成功执行。如果它返回非零退出码,PostgreSQL 将认为归档失败,这可能导致 WAL 文件无法被移除。确保目标目录有足够的空间,并且运行 PostgreSQL 的用户具有写入权限。
监控归档
使用 SQL 查询检查归档状态:
SELECT archived_count,
failed_count,
last_archived_wal,
last_archived_time,
last_failed_wal,
last_failed_time
FROM pg_stat_archiver;
如果 failed_count 持续增加,或者 last_archived_time 较旧而数据库仍在写入,请在调整 WAL 大小参数之前修复归档目标。
2. 管理 pg_wal 目录大小
即使启用了归档,如果 WAL 段在归档后未被移除,主节点上的 pg_wal 目录也可能增长。如果出现以下情况,就会发生这种情况:
- 备用节点跟不上,主节点为复制保留额外的 WAL。
- 复制槽持有 WAL 文件。
wal_keep_size
此参数在主节点上保留额外的 WAL 用于流复制。它取代了 PostgreSQL 13 中较旧的 wal_keep_segments 设置。对于不使用槽的备用节点很有用,但不能保证严重延迟的备用节点总能赶上。
# 主节点上的 postgresql.conf
wal_keep_size = 1024 # 在磁盘上保留 1GB 的 WAL
当您需要主节点为特定消费者保留 WAL 时,通常首选复制槽,但必须监控槽,因为它们可以无限期保留 WAL。
max_slot_wal_keep_size (PostgreSQL 13+)
此设置限制复制槽可以保留的 WAL 量。它是防止损坏槽无限增长的护栏,但也可能导致延迟的消费者丢失所需的 WAL 并需要重新初始化。
# 主节点上的 postgresql.conf
max_slot_wal_keep_size = 2048 # 限制槽保留 2GB 的 WAL
# 还要考虑:wal_keep_size -- 对于非基于槽的流仍然相关
# wal_keep_size = 1024 # 为非槽流保留 1GB
如果槽落后太多并超过此限制,PostgreSQL 可能会在检查点时间移除所需的 WAL。这保护了磁盘空间,但受影响的备用节点或逻辑复制客户端可能无法从其旧位置继续。
复制槽
复制槽对于防止 WAL 丢失和确保可靠复制至关重要。但是,如果管理不当,它们可能导致 WAL 文件累积。
- 问题: 创建了复制槽,但消费者(备用节点或逻辑客户端)断开连接或失败,并且槽从未被删除。主服务器将保留该槽等待的所有 WAL 文件。
- 解决方案: 定期监控复制槽并删除任何不再使用的槽。
-- 列出复制槽
SELECT slot_name,
plugin,
slot_type,
active,
restart_lsn,
wal_status,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained_wal
FROM pg_replication_slots
ORDER BY pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) DESC NULLS LAST;
-- 删除未使用的槽
SELECT pg_drop_replication_slot('slot_name_to_drop');
警告: 删除复制槽将导致任何连接的消费者丢失其位置。在删除之前,确保消费者不再需要或已正确重新初始化。
3. 调整 min_wal_size 和 max_wal_size
这些参数影响检查点频率以及 PostgreSQL 尝试保留多少 WAL 以供重用。它们不限制为归档或复制保留的 WAL。
min_wal_size:鼓励 PostgreSQL 至少保留这么多 WAL 以供重用,而不是立即移除。max_wal_size:倾向于触发检查点的 WAL 量。当 WAL 因其他原因被保留时,这不是硬性最大值。
# postgresql.conf
min_wal_size = 1GB
max_wal_size = 4GB
增加 max_wal_size 可以在峰值写入负载期间为系统提供更多余量,但也意味着预分配的 WAL 文件将占用更多磁盘空间。
4. 定期清理归档的 WAL 文件
WAL 归档虽然对恢复至关重要,但如果归档文件从未被清理,也可能导致磁盘空间问题。您必须有一个管理归档 WAL 文件保留的策略。
策略: 实现一个脚本或使用专用工具(如
pg_archivecleanup、pgBackRest、wal-g、barman)从归档位置移除不再需要用于 PITR 或复制的旧 WAL 文件。使用
pg_archivecleanup: 此实用程序可以在主服务器上运行,以从归档目录中移除旧的 WAL 文件。pg_archivecleanup /path/to/archive/location 0000000100000037000000AF第二个参数是需要保留的最旧 WAL 文件名,而不是任意年龄。实际上,像 pgBackRest、Barman 和 WAL-G 这样的备份工具更安全,因为它们了解备份保留和恢复所需的 WAL。
重要: 始终确保您的清理策略与您的备份和恢复时间点恢复 (PITR) 要求一致。您需要保留 WAL 文件足够长的时间以覆盖您所需的恢复窗口。
5. 监控磁盘空间和 WAL 生成速率
主动监控是防止磁盘空间耗尽的关键。
监控磁盘空间: 跟踪数据目录、
pg_wal、临时文件位置和归档目标中的可用空间。监控 WAL 生成: 使用随时间变化的 LSN 差异来估计生成速率。
SELECT now() AS sample_time, pg_current_wal_lsn() AS current_lsn;定期存储该值,并使用
pg_wal_lsn_diff(new_lsn, old_lsn)比较样本。要快速查看当前pg_wal目录大小:SELECT pg_size_pretty(sum(size)) AS pg_wal_size FROM pg_ls_waldir();
磁盘满的排查步骤
如果您的磁盘因 WAL 活动已满,需要立即采取行动:
- 识别原因: 检查
pg_stat_archiver是否存在归档失败。检查pg_replication_slots是否存在未使用或有问题的槽。检查备用节点上的复制延迟。 - 释放空间而不损害恢复:
- 不要手动删除
pg_wal中的文件。 - 如果归档目标已满,仅当旧归档 WAL 超出备份保留窗口时才移除它们。
- 如果可能,添加存储或移动归档目标,然后让 PostgreSQL 正常归档和回收。
- 不要手动删除
- 解决根本原因:
- 修复归档: 确保
archive_command正确且目标有空间。 - 管理槽: 删除任何未使用的复制槽。
- 修复复制: 解决导致备用节点延迟的问题。
- 增加磁盘空间: 临时或永久添加更多存储。
- 修复归档: 确保
- 推动归档器: 修复归档命令或目标后,PostgreSQL 应重试。对于配置更改,重新加载可能就足够了;在磁盘事件期间,完全重启应是最后的手段。
更安全的心智模型
当 pg_wal 增长时,按顺序问三个问题:
- PostgreSQL 是否因为工作负载变化而生成比平时更多的 WAL?
- PostgreSQL 是否无法归档 WAL?
- PostgreSQL 是否被告知为复制或槽保留 WAL?
这些答案指向不同的修复方法。批量写入可能需要调度、批处理或检查点调整。归档失败需要存储和命令修复。槽保留需要消费者恢复、槽清理或保留限制。猜测 max_wal_size 很少能单独解决实际问题。