PostgreSQL 教程: 处理 pg_wal 目录中的 WAL 积压

三月 1, 2025

摘要:在本教程中,您将学习如何在 PostgreSQL 中排查 pg_wal目录中的 WAL 积压问题。

目录

背景

PostgreSQL 用户最常见的一个困惑是:为什么我的pg_wal目录在增长?或者pg_wal填满了文件系统,或者pg_wal不释放空间,诸如此类。

在 PostgreSQL 中,在管理预写式日志(WAL)的机制方面,pg_wal目录起着至关重要的作用,该机制可确保事务的持久性和崩溃恢复。WAL 文件还可服务于复制的目的(如果有)。

管理员可能会遇到pg_wal目录积压文件的情况,逐渐消耗磁盘空间,并可能引发文件系统问题和数据库崩溃。

pg_wal磁盘填满了后,这意味着没有更多空间来写入其他更改了,因此 PostgreSQL 做了它唯一能做的事情:它会崩溃,停止服务器和断开所有的连接;一个无法进行更多更改的数据库,没有多大的用处。

要记住的重要一点是,即使 PostgreSQL 由于pg_wal磁盘已满而关闭并且不会再次启动,此时您的数据库也没有损坏。您仍然可以采取一些措施来启动数据库,而不会丢失或损坏数据。

本教程将介绍pg_wal目录空间增长的一些常见原因、如何进行故障处理以及缓解该问题的策略。

WAL 归档工作原理的简要概述

  • 当 PostgreSQL 需要对数据进行更改时,它会先将此更改记录在预写日志(WAL)中,并将 WAL 记录fsync到磁盘中。fsync是一个系统调用,它可以保证写入的数据已完全写入到持久性存储,而不仅仅是存储在文件系统缓存中。在将此 WAL 成功写入磁盘后,即使数据库在这次写入和实际更改数据目录之间崩溃,数据库也将能够通过重放存储在 WAL 中的更改来恢复到一致状态。
  • 在将来的某个时刻,数据库将会发起一次检查点,它会将所有修改的缓冲页刷写到磁盘,并将更改永久存储到数据目录中。在检查点完成后,数据库会记录这个操作发生时的 WAL 位置,确定到该位置为止的所有这些数据都已成功写入到磁盘。(值得注意的是,通过检查点已经写入到磁盘的任何更改,都已将其内容先前记录到了 WAL 流中,因此这只是确保 WAL 数据记录的更改在事实上应用到实际磁盘上的关系文件的一种方式。
  • WAL 数据以 16MB 的块存储在磁盘文件中;当数据库生成 WAL 文件时,它会对它们运行archive_command,以将这些文件存储到数据库以外的地方。archive_command的退出码用于确定 PostgreSQL 执行此操作是否成功。在archive_command执行成功后,PostgreSQL 会认为已经成功处理了该 WAL 段,从而可以将其删除或回收以节省空间。如果archive_command执行失败,PostgreSQL 会保留 WAL 文件并无限期地重试,直到此命令成功。即使特定的段文件失败,PostgreSQL 也会继续为所有其他的数据库更改累积 WAL 文件,并继续在后台重试归档之前的文件。

如您所见,如果 archive_command 出现问题,这可能会导致 WAL 累积,因为 PostgreSQL 在确认 archive_command 接收到这些 WAL 段之前不会删除任何段。

当 PostgreSQL 服务器启动时,它会查看pg_control文件以确定最后一次检查点的时间,并将重放自上一次检查点以来在数据库中生成的所有 WAL。这可确保数据库达到一致的状态,该状态将会反映 WAL 中记录和做过的所有数据更改。

WAL 文件是按顺序命名的,文件名由多个部分组成,它是根据数据库中所做的更改的顺序定义的。您可以将数据库更改的整个历史视为从开始一直应用所有 WAL 文件所做出的。但是,由于随着时间的推移会有太多的更改需要重放,PostgreSQL 基本上使用检查点作为一个点,来确定删除/回收 WAL 文件以及从这一点开始是安全的。

就像archive_command失败会无限期地保留 WAL 文件一样,由于复制槽的消费端离线而让复制槽未更新,将会阻止 WAL 被删除,并成为影响磁盘填满的一个因素 。PostgreSQL 13 及更高版本可以使用 max_slot_wal_keep_size 设置来防止这种特定的情况。这样做的好处是限制了 WAL 会占用的磁盘空间(从而防止主服务器因存储空间不足而关闭),但代价是如果您确实达到了该限制,则需要重建副本,因为主服务器已经删除了它往前推进所需的 WAL。

事务量高峰期

问题:事务速率高,或事务量高峰期。WAL 段是 PostgreSQL 在处理事务时,在写入数据文件之前而生成的。如果事务速率超过系统归档或删除这些段的速率,它们会积压在 pg_wal 目录中,从而导致磁盘空间耗尽。

尽管归档速度通常不重要,但它与 pg_wal 目录中的平均 WAL 生成速率至少应该保持同步。如果归档速度明显落后于 WAL 段创建速率太长时间,pg_wal 将开始积压文件,直到归档完为止。如果 pg_wal 没有足够的空间,以容纳一些不常见/计划外的负载,则它可能会耗尽空间。

故障排除:确定 pg_wal 的 WAL 段创建速率,并将其与归档速度进行比较。

解决方案:如果归档速度不够快,我们应该改进归档的 IO 子系统(或使用 S3 云服务时的网络延迟)。否则,需要评估业务负载峰值的持续时间,并为 pg_wal 增加额外的空间。

低效的检查点

问题:PostgreSQL 中的检查点,对于将修改后的数据从内存刷新到磁盘,和回收过时的 WAL 段,都有重要作用。但是,效率低下的检查点策略,例如太不频繁或过于激进的检查点,可能会引起 WAL 文件的积压。不频繁的检查点会导致 WAL 段的保留时间延长,而过于激进的检查点可能会导致过多的磁盘 I/O 和 WAL 生成。

故障排除:评估有关数据库业务负载的检查点和 WAL 参数(min_wal_sizemax_wal_sizewal_keep_size/wal_keep_segmentsbgwriter_lru_maxpagesbgwriter_delay 等)。

解决方案:为检查点频率和后台写进程的效率找到适当的权衡。

复制延迟

范围:只有在设置了流复制时,才可能由复制延迟引发 WAL 积压。

问题:在备用服务器上应用更改的延迟,可能会加剧主服务器上 WAL 文件的积压。由于网络问题/速度缓慢、高负载或硬件资源限制,备用服务器可能会落后于其主服务器,因此主服务器会保留 WAL 段,直到它们在备用服务器上成功完成重放。此延迟可能会使主服务器上的磁盘空间的可用量变得紧张。

上述原因以及 wal_keep_size/wal_keep_segments 参数的配置错误,可能会导致空间耗尽。

废弃的复制槽会无限期地保留 WAL 段。

故障排除:验证主数据库和备用数据库之间的复制延迟。验证 wal_keep_size/wal_keep_segments(取决于您的数据库版本)参数的配置。在主服务器中查找已废弃的复制槽

解决方案:提升备用服务器上的网络性能或 IO 性能(或任何硬件瓶颈)。删除任何已废弃的复制槽。根据复制性能和主服务器 pg_wal 目录容量,调整 wal_keep_size/wal_keep_segments 参数的配置。

WAL 归档异常

范围:只有在数据库启用了连续归档时(archive_mode 设置为 on,并且也设置了 archive_command),才可能由归档异常引发 WAL 积压。

问题:如果归档进程无法执行在 archive_command 中设置的命令,则 WAL 段将保留在 pg_wal 目录中,直到归档成功。

归档异常最常见的原因可能有:归档存储库中的磁盘空间可能不足;归档目标无法访问;SSH 密钥可能已更改;归档工具版本可能在存储库服务器上已更新,而主数据库服务器上没有进行相应的更新;或者有许多其他可能性。

故障排除:每当 archive_command 执行失败时,都会在 PostgreSQL 日志中记录一条错误消息。

解决方案:这取决于确定WAL 归档异常的原因,再修复它。

WAL 保留策略

问题:WAL 归档的保留策略配置错误或不合理,也会导致 pg_wal 目录中文件的积压。如果归档进程未能及时删除过时的 WAL 段,则目录可能会因不必要的文件而变得膨胀,从而占用可用于其他目的的磁盘空间。

故障排除:查看上述 min_wal_sizemax_wal_sizewal_keep_size/wal_keep_segments 参数。查看 PostgreSQL 日志中是否有失败的归档事件。

解决方案:改进相关参数,并修复归档失败的原因。

结论

PostgreSQL 中的 pg_wal 目录,对于确保数据持久性和恢复至关重要,但其不受限制的增长可能会导致磁盘空间限制和操作上的挑战。通过了解 pg_wal 目录中文件积压的常见原因,并实施适当的策略(例如调整检查点设置、监控复制延迟和实施有效的 WAL 保留策略),管理员可以有效地管理磁盘空间的使用量,并维护其 PostgreSQL 数据库的稳定性和性能。

由于 WAL 段在 PostgreSQL 数据库中起着关键作用,因此您永远不应该从 pg_wal 中手动删除 WAL 段。它可能会导致数据库崩溃、崩溃恢复失败、WAL 归档失败以及备份数据不完整。

了解更多

PostgreSQL 管理

监控 WAL 文件