PostgreSQL运行一段时间后,你会发现你pg_wal目录在持续增长,如果磁盘空间不足,数据库服务就可能会因为pg_wal增长而崩溃。
pg_wal是什么,为什么会增长?
PostgreSQL会将数据库的所有修改记录在事务重做日志中,这种日志被称为预写日志(WAL)。在将数据修改应用到数据文件之前,PostgreSQL会先记录这些修改。这样做有三个目的:
用于从崩溃中恢复数据,确保不会丢失任何数据
用于在恢复文件系统备份后,恢复数据
用于在备用服务器上重放数据
PostgreSQL将WAL按顺序写入到 data 目录下的 pg_wal子目录中的 WAL段文件。每个 WAL段的大小通常是 16MB(这个大小可以在初始化数据库时设置)。WAL段通常处于以下三种状态之一:
活动状态:PostgreSQL当前正在向这个文件写入事务重做信息
空闲状态:一些 WAL段是空的,留着以后使用,避免在活动 WAL段满时停顿
完成状态:一些 WAL段已完成,不再用于正常操作
我们关心的是完成状态的WAL段。PostgreSQL会在不再需要它们时自动删除这些文件。要解决 pg_wal持续增长的问题,需要更好地理解这个过程。警告:不要手动删除pg_wal中的任何文件。
PostgreSQL需要这些文件来从崩溃中恢复。磁盘空间不足,需要增加磁盘空间。
PostgreSQL什么时候从 pg_wal中删除WAL段?
PostgreSQL在检查点结束时删除不需要的已完成 WAL段。在删除 WAL段之前,PostgreSQL会检查以下三个条件:
1. pg_wal中的 WAL段已成功存档
这仅适用于已将 archive_mode设置为 on的情况。然后,存档器会执行 archive_command(或调用 archive_library)来存档 WAL段。如果存档程序失败,会不断重试,直到成功。因为 PostgreSQL无法恢复丢失的 WAL段。要查看存档程序是否卡住,可以使用以下查询:
SELECT last_failed_wal, last_failed_time FROM pg_stat_archiver WHERE last_failed_time > coalesce(last_archived_time, '-infinity');
如果查询返回结果,请查看 PostgreSQL日志文件,那里会显示存档程序的所有错误消息。修复问题后,PostgreSQL将在下一个检查点后开始删除旧的 WAL段。
有时问题不在于存档器卡住了,而是它太慢了:存档 WAL段的速度比 PostgreSQL生成它们的速度慢。造成这种情况的一个原因可能是 archive_command使用了慢速压缩方法。这种情况下,需要加快 archive_command来解决问题。
2. pg_wal中的 WAL段不包含比任何复制槽位置更新的数据
备用服务器需要从主服务器获取 WAL信息。如果数据不再位于 WAL缓冲区中,WAL发送器进程将从 WAL段文件中读取它们。如果备用数据库所需的 WAL信息不再存在,复制将会中断。复制槽是一种在 WAL中标记位置的数据结构。每当备用服务器使用 WAL时,都会向主服务器发送反馈,从而推进与复制关联的复制槽。主数据库永远不会删除比任何复制槽更新的 WAL。
可以查询最旧的复制槽后面有多少字节:
SELECT slot_name, pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) AS bytes_behind, active, wal_status FROM pg_replication_slots WHERE wal_status <> 'lost' ORDER BY restart_lsn;
如果发现一个复制槽远远落后,就找到了问题所在。通常,这样的复制槽不再活跃,因为备用服务器不再连接到主服务器。使用函数 pg_drop_replication_slot()删除有问题的复制槽,PostgreSQL将在下一个检查点后自动删除旧的 WAL段。
为了防止落后或已被放弃的复制槽破坏数据库,可以设置参数 max_slot_wal_keep_size。PostgreSQL保留的 WAL不会超过复制槽的 WAL。如果复制槽超过限制,最终会在 pg_replication_slots中显示为wal_status = 'lost'。
3. wal_keep_size设置得太高
参数 wal_keep_size决定了保留多少旧的 WAL。可以作为使用复制槽的替代方法,有助于使pg_rewind成功。因此,请检查是否将该参数设置得过高:
SHOW wal_keep_size;
如果这是问题所在,请减小该值,PostgreSQL将在下一个检查点后删除旧的 WAL段。
结论
如果 pg_wal溢出,请检查存档程序问题、滞后的复制槽和过高的 wal_keep_size。这应该可以解决问题。如果不想等待下一个检查点从 pg_wal中删除文件,可以手动运行 CHECKPOINT命令。




