30.4. WAL 配置

有几个与 WAL 相关的配置参数会影响数据库性能。本节说明其用法。有关设置服务器配置参数的一般信息,请咨询Chapter 19

检查点 是事务序列中的点,可以保证在该事务点中已使用在该检查点之前写入的所有信息来更新堆和索引数据文件。在检查点时间,所有脏数据页都将刷新到磁盘,并将特殊的检查点记录写入日志文件。 (更改记录之前已刷新到 WAL 文件.)发生崩溃时,崩溃恢复过程将查看最新的检查点记录,以确定日志中应从其开始启动的点(称为重做记录)。 REDO 操作。在此之前,对数据文件所做的任何更改都可以保证已经在磁盘上。因此,在检查点之后,不再需要包含重做记录的日志段,而可以将其回收或删除。 (完成 WAL 归档后,必须先对日志段进行归档,然后再进行回收或删除.)

将所有脏数据页刷新到磁盘的检查点要求可能会导致大量的 I/O 负载。因此,检查点活动受到限制,因此 I/O 在检查点开始时开始,并在下一个检查点应开始之前完成。这样可以最大程度地减少检查点期间的性能下降。

服务器的检查点进程会每隔一段时间自动执行一个检查点。每checkpoint_timeout秒或如果将要超过max_wal_size时,将开始一个检查点,以先到者为准。默认设置分别为 5 分钟和 1 GB。如果自上一个检查点以来未写入 WAL,则即使checkpoint_timeout已通过,也将跳过新的检查点。 (如果正在使用 WAL 归档,并且您希望对文件的存储频率设置较低的限制以限制潜在的数据丢失,则应调整archive_timeout参数而不是检查点参数。)也可以强制使用检查点通过使用 SQL 命令CHECKPOINT

减少checkpoint_timeout和/或max_wal_size会导致检查点更频繁地出现。由于需要重做的工作量少,因此可以更快地实现崩溃后的恢复。但是,必须平衡这一点与更频繁地刷新脏数据页所增加的成本。如果设置了full_page_writes(默认设置),则需要考虑另一个因素。为了确保数据页的一致性,在每个检查点之后对数据页进行的第一次修改将导致记录整个页面的内容。在那种情况下,较小的检查点间隔会增加向 WAL 日志的输出量,从而部分抵消了使用较小间隔的目标,并且无论如何都会导致更多的磁盘 I/O。

检查点相当昂贵,首先是因为它们需要写出所有当前脏缓冲区,其次是因为它们会导致额外的后续 WAL 通信,如上所述。因此,明智的做法是将检查点参数设置得足够高,以使检查点不会经常发生。作为对检查点参数的简单检查,可以设置checkpoint_warning参数。如果检查点的间隔比checkpoint_warning秒更近,则会向服务器日志输出一条消息,建议增加max_wal_size。偶尔出现此类消息不会引起警报,但如果经常出现,则应增加检查点控制参数。如果未将max_wal_size设置得足够高,则诸如大COPY传输之类的批量操作可能会导致出现许多此类警告。

为避免大量的页面写入大量 InfluxI/O 系统,在检查点期间写入脏缓冲区的时间会分散。该时间段由checkpoint_completion_target控制,它是检查点间隔的一部分。调整 I/O 速率,以便在经过checkpoint_timeout秒的给定分数时或在超过max_wal_size之前(以较早者为准),检查点完成。默认值为 0.5,可以期望 PostgreSQL 在下一个检查点开始之前的大约一半时间内完成每个检查点。在非常接近正常操作期间最大 I/O 吞吐量的系统上,您可能需要增加checkpoint_completion_target以减少来自检查点的 I/O 负载。这样做的缺点是延长检查点会影响恢复时间,因为将需要保留更多的 WAL 段以用于恢复。尽管checkpoint_completion_target可以设置为 1.0,但最好使其小于此值(最多为 0.9),因为检查点除了写入脏缓冲区之外还包括其他活动。设置为 1.0 很有可能导致检查点无法按时完成,这将由于所需 WAL 段数的意外变化而导致性能损失。

在 Linux 和 POSIX 平台上,checkpoint_flush_after允许强制 os 在可配置的字节数后将检查点写入的页面刷新到磁盘。否则,这些页面可能会保留在 OS 的页面缓存中,从而在检查点末尾发出fsync时导致停顿。此设置通常有助于减少事务延迟,但也可能对性能产生不利影响。特别是对于大于shared_buffers,但小于 os 页面缓存的工作负载。

pg_wal目录中的 WAL 段文件的数量取决于min_wal_sizemax_wal_size和先前检查点周期中生成的 WAL 数量。当不再需要旧的日志段文件时,会将它们删除或回收(即,重命名以按编号 Sequences 成为将来的段)。如果由于日志输出速率的短期峰值而超过max_wal_size,则将删除不需要的段文件,直到系统恢复到此限制以下。低于该限制,系统将回收足够的 WAL 文件以满足估计的需求,直到下一个检查点为止,然后删除其余的文件。该估算基于先前检查点周期中使用的 WAL 文件数量的移动平均值。如果实际使用量超出了估计量,则移动平均立即增加,因此它在某种程度上适应了峰值使用量,而不是平均使用量。 min_wal_size对回收的 WAL 文件数量进行了最小化以供将来使用;即使系统处于空闲状态,并且 WAL 的使用量估计表明几乎不需要 WAL,也总是回收大量的 WAL 以供将来使用。

独立于max_wal_sizewal_keep_segments 1 始终保留最新的 WAL 文件。同样,如果使用 WAL 归档,则在归档之前,不能删除或回收旧段。如果 WAL 归档无法跟上 WAL 生成的速度,或者archive_command反复失败,则旧的 WAL 文件将在pg_wal中累积,直到情况解决。使用复制插槽的慢速或故障备用服务器将具有相同的效果(请参阅Section 26.2.6)。

在归档恢复或备用模式下,服务器会定期执行* restartpoints *,,这与正常操作中的检查点类似:服务器将其所有状态强制为磁盘,更新pg_control文件以指示不需要处理已处理的 WAL 数据。再次扫描,然后回收pg_wal目录中的所有旧日志段文件。重新启动点不能比主服务器中的检查点更频繁地执行,因为重新启动点只能在检查点 Logging 执行。如果自上次重新启动点以来至少经过checkpoint_timeout秒,或者如果 WAL 大小即将超过max_wal_size,则在到达检查点记录时触发重新启动点。但是,由于在何时可以执行重新启动点方面存在限制,因此在恢复过程中经常会超过max_wal_size WAL。 (max_wal_size绝对不是硬性限制,因此您应该始终留有足够的净空,以避免用完磁盘空间.)

有两种常用的内部 WAL 函数:XLogInsertRecordXLogFlushXLogInsertRecord用于将新记录放入共享内存中的 WAL 缓冲区中。如果新记录没有空间,XLogInsertRecord将必须写入(移至内核高速缓存)一些已填充的 WAL 缓冲区。这是不希望的,因为在受影响的数据页上保留排他锁时,每次数据库低级修改(例如,行插入)都使用XLogInsertRecord,因此操作需要尽可能快。更糟糕的是,编写 WAL 缓冲区也可能会强制创建新的日志段,这会花费更多时间。通常,应通过XLogFlush请求写入和刷新 WAL 缓冲区,该请求在大多数情况下是在事务提交时发出的,以确保将事务记录刷新到永久存储中。在具有高日志输出的系统上,XLogFlush请求的发生频率可能不足以阻止XLogInsertRecord进行写操作。在此类系统上,应通过修改wal_buffers参数来增加 WAL 缓冲区的数量。当设置full_page_writes且系统非常忙时,将wal_buffers设置得更高将有助于在每个检查点之后的一段时间内平滑响应时间。

commit_delay参数定义在XLogFlush内获得锁之后,组提交领导者进程将休眠多少微秒,而组提交跟随者在领导者后面排队。此延迟使其他服务器进程可以将其提交记录添加到 WAL 缓冲区,以便所有这些进程都可以通过领导者的最终同步操作来刷新。如果未启用fsync或当前正在进行的事务中的其他会话少于commit_siblings个,则不会发生睡眠。这样可以避免在其他会话不太可能很快提交时休眠。请注意,在某些平台上,睡眠请求的分辨率为 10 毫秒,因此 1 到 10000 微秒之间的任何非零commit_delay设置都将具有相同的效果。还要注意,在某些平台上,睡眠操作所花费的时间可能比该参数所请求的时间稍长。

由于commit_delay的目的是允许在同时提交的事务之间分摊每个刷新操作的成本(可能以事务 await 时间为代价),因此有必要在可以智能选择设置之前量化该成本。成本越高,则在某种程度上提高commit_delay的 Transaction 吞吐量预期越有效。 pg_test_fsync程序可用于测量单个 WAL 刷新操作所花费的平均时间(以微秒为单位)。 commit_delay的最有效设置通常是程序报告在一次 8kB 写入操作后刷新所花费的平均时间的一半,因此建议将该值作为针对特定工作负载进行优化时使用的起点。当将 WAL 日志存储在高延迟旋转磁盘上时,调优commit_delay特别有用,即使在同步时间非常快的存储介质(例如固态驱动器或具有 Batteries 后备写入缓存的 RAID 阵列)上,好处也很明显。但这绝对应该针对具有代表性的工作负载进行测试。在这种情况下,应使用较高的commit_siblings值,而较小的commit_siblings值通常有助于较高延迟的媒体。请注意,commit_delay的设置过高很可能会增加事务延迟,从而使总事务吞吐量受到影响。

commit_delay设置为零(默认值)时,仍然有可能发生某种形式的组提交,但是每个组将仅由会话组成,这些会话到达需要在窗口中刷新提交记录的点。发生先前的冲洗操作(如果有)。在较高的 Client 数量下,往往会发生“通道效应”,因此,即使commit_delay为零,组提交的影响也变得很明显,因此,明确设置commit_delay往往会有所帮助。设置commit_delay只能在以下情况下提供帮助:(1)有一些同时提交的事务,并且(2)吞吐量在一定程度上受提交速率的限制;但此设置具有很高的轮转延迟时间,可以有效地增加只有两个 Client 端的事务吞吐量(即,一个提交 Client 端具有一个同级事务)。

wal_sync_method参数确定 PostgreSQL 如何要求内核将 WAL 更新强制输出到磁盘。除了fsync_writethrough之外,所有选项在可靠性方面都应该相同,即使fsync_writethrough有时可能会强制刷新磁盘缓存,即使其他选项没有这样做。但是,它是特定于平台的,是最快的。您可以使用pg_test_fsync程序测试不同选项的速度。请注意,如果已关闭fsync,则此参数无关紧要。

启用wal_debug配置参数(假设 PostgreSQL 已经编译并支持它)将导致每个XLogInsertRecordXLogFlush WAL 调用都记录到服务器日志中。将来可能会用更通用的机制代替此选项。