为什么PostgreSQL WAL归档很慢
2020-12-22 14:59 abce 阅读(638) 评论(0) 编辑 收藏 举报在客户中,经常会遇到由于大量的WAL段占据了WAL目录(pg_wal目录),导致磁盘空间使用量突然暴增的案例。慌张的客户通常会问:“为什么PostgreSQL不删除它们呢?”
我们发现最常见的原因是:
1.WAL归档失败
2.复制槽正在持有旧的WAL
然而,近些年来,在与此类似的主题下,出现了另一种不同类型的案例。很显然,“慢”是带有主观性的,而且大多数用户会将“慢”与WAL段的生成速度相比。这种案例的增加主要是由于单台服务器的处理能力的提高,PostgreSQL的可扩展性不断提高(例如分区功能的最新改进,批量数据加载的改进等)以及更快的新一代存储的使用而引起的。基本上,单台服务器能做的工作越来越多。因此,大量的WAL被生成也逐渐变成常规现象。这一点,与为什么WAL压缩正在成为迫切需求的原因是一样的(参考原文:https://www.percona.com/blog/2020/02/13/compression-of-postgresql-wal-archives-becoming-more-important/)。像WAL-G和pgBackRest这样的备份解决方案如何通过内置的压缩特性解决这个问题也在那篇博客文章中讨论了。
同时,由于相比于昂贵的备份设备的价格优势和经过时间验证的可靠性,远程云存储正成为存储归档WAL的更具吸引力的选择。而且,用户/组织对云存储也越来越熟悉,舒适度的提高是决策使用云存储做备份的主要驱动力。
但是,这种WAL段的快速生成,结合使用缓慢/远程的存储作为归档存放位置是整个WAL归档过程的致命组合。除非进行适当的监控和处理,否则可能会导致灾难。
在本文中,我们将详细了解归档进程如何工作以及如何以同步方式处理archive_command中指定的外部shell命令。 此外,我们将尝试查看在WAL的同步归档处理过程中的特定区域,以及它如何影响归档速度并成为一项挑战的。
同步的WAL归档
PostgresSQL的WAL归档非常灵活,因为它可以通过为参数archive_command指定外部的shell命令。这种特性可以执行上一篇文章中讨论的任何自定义归档脚本。让我们看一下WAL归档是如何开始的。
通常,归档的整个事件链都是WAL写(XLogWrite())的一部分。当一个WAL段文件完成后,通过将.ready文件插入pg_wal目录下的archive_status目录中,通知归档进程对这个段有一些工作要处理(内部函数XLogArchiveNotifySeg-> XLogArchiveNotify)。例如,如果要归档的WAL段是0000000100000001000000C6,则.ready文件将是0000000100000001000000C6.ready。该.ready文件充当归档进程的通知文件。除了创建此文件外,还将信号发送到归档进程以进行唤醒(将SIGUSR1发送到归档进程)。现在,归档进程可以醒来并开始处理所有.ready文件。
与归档进程的所有直接通信都是通过信号进行的。归档进程收到信号(SIGUSR1)后,就知道有一些工作要做了。然后,归档进程开始浏览后缀为.ready的每个文件,并找到需要复制的最旧的段文件。最早的WAL段应首先归档是最重要的,因为:
1.这将在需要执行还原时有所帮助,因为是按照顺序应用归档的WAL文件。如果两者之间缺少任何WAL,则可恢复性将受到影响。
2.发生检查点时,最早的WAL段将有更大的机会被回收。 因此,它丢失的机会更大。
现在,让我们看一下整个操作中的主要瓶颈。
原因1:
让我们看第一个问题。一个接一个地找出最旧的WAL段并将其逐个归档的方法不是很有效。
对于每次迭代,归档进程都需要遍历完整的.ready文件列表以查找最早的文件。在正常情况下,这不是大问题。但是,在许多高活动性服务器和慢速备份存储中,我们遇到的归档滞后了成千上万个WAL段文件。在这种情况下,执行目录列表和遍历.ready文件变得非常低效,这会降低WAL归档的速度,这已经很滞后了。 如果不注意,累积效应会导致更危险的情况。
原因2:
慢的第二个问题从这里开始。一旦确认了段文件,就需要将其归档。调用内部函数pgarch_archiveXlog(),它将调用system()系统调用以执行archive_command指定的外部命令/脚本。此命令通常使用2个参数-%p(源段文件的相对路径)和%f(用于指定源段文件的文件名)。一旦通过system()调用执行了外部Shell命令,便会检查其返回值以了解执行是否成功(已归档WAL)或失败。基本上,归档进程等待外部命令的返回。如果外部脚本由于某种原因而具有执行延迟,则所有延迟将累加起来。
原因3:
如果在执行archive_command时底层系统出现故障/超时,则归档程序将再等待一秒钟再尝试。因此速度很慢,通过WAN连接的存储可能要等待更多时间。仅当上一个成功时,WAL段才会被归档。一旦外部shell命令成功返回(如上所述,pgarch_archiveXlog()函数已成功),则此.ready通知文件将由归档器pgarch_archiveDone()重命名为.done。
这里的常见问题之一是“我们需要编写脚本来删除已归档的WAL段和.done文件吗?”答案是否定的。检查点进程将为你完成这一任务。它将删除.done和相应的WAL段文件(包括循环使用WAL段文件)。如果archive_staus目录中剩余任何.ready文件,而相应的WAL段已经被回收或移除,那么这些.ready文件将由归档进程自己来删除。
正如我们所讨论的,只有两种通知状态-.ready或.done。归档没有“进行中(in-progress)”的通知状态,如果需要同时进行多个归档,则这是必不可少的。这是通过精心设计,它不存在。除非WAL归档成功,并且.ready文件重命名为.done,否则我们认为归档从未发生过。因此,如果在两者之间发生任何故障,PostgreSQL将再次尝试归档(有时会重新复制同一文件)。
诸如pgBackRest之类的高级备份解决方案具有异步备份功能,该功能将允许多个后台工作进程进行压缩和WAL归档推送,同时前端将确认反馈给PostgreSQL。我们将在即将发表的博客文章中对此进行介绍。
总结
最近,WAL归档的同步操作变得越来越痛苦。对于每个未完成归档的WAL,WAL归档的整个操作将循环地一个接一个地进行,直到不再有WAL段要归档为止。如果WAL段的生成速率超过归档速率,则WAL段很可能堆积在pg_wal目录中,并且随着.ready文件数量的增加,问题变得更加严重。 归档进程一旦接收到信号(SIGUSR1),便会唤醒并执行上面讨论的所有迭代,并且该过程将一直进行到接收到SIGUSR2。没有内置机制可以使其异步。但是,由于PostgreSQL使用外部命令/脚本进行归档操作,因此灵活的程序/脚本可以将整个同步操作转换为异步操作。这里强调了需要一种备份工具,该工具可以以异步方式推送WAL归档。