有几个WAL-相关的配置参数影响数据库性能。本节解释其使用。有关设置服务器配置参数的常规信息,请参见 第 19 章。
检查点 是事务序列中的点,在该点保证堆和索引数据文件已更新为在该检查点之前写入的所有信息。在检查点时,所有脏数据页被刷新到磁盘,并将一个特殊的检查点记录写入 WAL 文件。(更改记录以前被刷新到WAL文件。)在发生崩溃的情况下,崩溃恢复过程会查看最新的检查点记录,以确定应从 WAL 中的哪个点(称为重做记录)开始 REDO 操作。在该点之前对数据文件进行的任何更改都保证已经存在于磁盘上。因此,在检查点之后,包含重做记录之前的 WAL 段不再需要,可以被回收或删除。(当WAL正在进行归档时,必须在回收或删除 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
传输)可能会导致出现许多此类警告。
为了避免用大量页写入洪泛 I/O 系统,在检查点期间写入脏缓冲区的时间会分散在一段时间内。这段时间由 checkpoint_completion_target 控制,它以检查点间隔的几分之一表示(通过使用 checkpoint_timeout
配置)。调整 I/O 速率,以便检查点在指定的 checkpoint_timeout
秒的几分之一时间过去时完成,或者在超过 max_wal_size
之前完成,以先发生者为准。使用默认值 0.9,PostgreSQL 可以预期在每次计划的检查点之前完成每个检查点(大约在上次检查点持续时间的 90%)。这尽可能地分散了 I/O,以便在整个检查点间隔内检查点 I/O 负载保持一致。这样做会导致延长检查点时间影响恢复时间,因为需要保留更多 WAL 段以供恢复时使用。如果用户担心恢复所需的时间,则可能希望减少 checkpoint_timeout
,以便更频繁地执行检查点,但仍然将 I/O 分散在整个检查点间隔内。或者,可以减少 checkpoint_completion_target
,但这会导致 I/O 更加集中(在检查点期间)以及 I/O 减少(在检查点完成但下一个计划的检查点开始之前),因此不建议这样做。虽然 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_size
、max_wal_size
以及之前检查点周期中生成的 WAL 数量。当不再需要旧的 WAL 段文件时,它们将被删除或回收(即,重命名为将来编号序列中的段)。如果由于 WAL 输出速率的短期峰值,超过了 max_wal_size
,则将删除不必要的段文件,直到系统恢复到此限制以下。在该限制以下,系统会回收足够多的 WAL 文件,以满足直到下一个检查点估计的需要,并删除其余文件。该估计基于之前检查点周期中使用的 WAL 文件数量的移动平均值。如果实际使用量超过估计值,则移动平均值会立即增加,因此它在一定程度上可以适应峰值使用量,而不是平均使用量。 min_wal_size
对回收用于未来使用量的 WAL 文件数量设置了最小值;即使系统处于空闲状态并且 WAL 使用量估计表明不需要太多 WAL,也要回收这么多的 WAL 以供将来使用。
与 max_wal_size
无关,始终保留最新的 wal_keep_size 兆字节的 WAL 文件加上一个额外的 WAL 文件。此外,如果使用 WAL 归档,则必须先归档旧段才能删除或回收它们。如果 WAL 归档跟不上 WAL 生成的速度,或者如果 archive_command
或 archive_library
反复失败,则旧的 WAL 文件将累积在 pg_wal
中,直到情况得到解决。使用复制槽的缓慢或失败的备用服务器也会产生相同的效果(参见 第 26.2.6 节)。
在归档恢复或备用模式下,服务器会定期执行 重启点, 这类似于正常运行时的检查点:服务器将所有状态强制写入磁盘,更新 pg_control
文件以指示已处理的 WAL 数据无需再次扫描,然后回收 pg_wal
目录中的所有旧 WAL 段文件。重启点不能比主服务器上的检查点执行得更频繁,因为重启点只能在检查点记录处执行。重启点可以由计划或外部请求触发。restartpoints_timed
计数器位于 pg_stat_checkpointer
视图中,它统计第一个计数器,而 restartpoints_req
统计第二个计数器。当达到检查点记录时,如果自上次执行重启点以来至少经过了 checkpoint_timeout 秒,或者上次尝试执行重启点失败,则由计划触发重启点。在最后一种情况下,下一个重启点将在 15 秒内安排。由于与检查点类似的原因,重启点会因请求而触发,但主要是因为 WAL 大小即将超过 max_wal_size。但是,由于重启点执行时间的限制,max_wal_size
在恢复期间经常被超过,最多超过一个检查点周期的 WAL。(max_wal_size
永远不是硬性限制,因此您应该始终留出足够的余量以避免磁盘空间不足。) restartpoints_done
计数器位于 pg_stat_checkpointer
视图中,它统计实际执行的重启点。
在某些情况下,当主服务器上的 WAL 大小快速增加时(例如,在大量 INSERT 操作期间),备用服务器上的 restartpoints_req
计数器可能会显示出峰值增长。这是因为由于 XLOG 使用量增加而创建新的重启点的请求无法执行,因为自上次重启点以来的安全检查点记录尚未在备用服务器上重放。这种行为是正常的,不会导致系统资源消耗增加。只有重启点相关计数器中的 restartpoints_done
计数器表示消耗了显著的系统资源。
有两个常用的内部WAL函数:XLogInsertRecord
和 XLogFlush
。 XLogInsertRecord
用于将新记录放入WAL共享内存中的缓冲区。如果新记录没有空间,XLogInsertRecord
将不得不写入(移动到内核缓存)几个已填充的WAL缓冲区。这是不可取的,因为 XLogInsertRecord
在每次数据库低级修改(例如,行插入)时使用,此时对受影响的数据页保持独占锁,因此操作需要尽可能快。更糟糕的是,写入WAL缓冲区也可能强制创建新的 WAL 段,这将花费更多时间。通常情况下,WAL缓冲区应由 XLogFlush
请求写入和刷新,该请求主要在事务提交时发出,以确保事务记录被刷新到永久存储。在 WAL 输出量大的系统中,XLogFlush
请求可能不会频繁地发生,以防止 XLogInsertRecord
必须执行写入操作。在这些系统中,应通过修改 wal_buffers 参数来增加WAL缓冲区的数量。当 full_page_writes 设置为 ON 且系统非常繁忙时,将 wal_buffers
设置为更高值将有助于在每次检查点后的时间段内平滑响应时间。
commit_delay 参数定义了组提交领导者进程在 XLogFlush
中获取锁后休眠的微秒数,而组提交跟随者在领导者后面排队。这种延迟允许其他服务器进程将其提交记录添加到 WAL 缓冲区,以便它们都将在领导者最终的同步操作中被刷新。如果未启用 fsync,或者当前只有不到 commit_siblings 个其他会话处于活动事务中,则不会发生休眠;这避免了在不太可能很快有其他会话提交时休眠。请注意,在某些平台上,休眠请求的分辨率为 10 毫秒,因此任何介于 1 到 10000 微秒之间的非零 commit_delay
设置将具有相同的效果。另外请注意,在某些平台上,休眠操作可能比参数请求的时间略长。
由于 commit_delay
的目的是允许每次刷新操作的成本在并发提交的事务之间摊销(可能以事务延迟为代价),因此有必要在智能选择设置之前量化该成本。成本越高,commit_delay
在提高事务吞吐量方面预计越有效,直到一定程度。 pg_test_fsync 程序可用于测量单个 WAL 刷新操作的平均时间(以微秒为单位)。程序报告的单个 8kB 写入操作后刷新所需时间的平均时间的一半值通常是 commit_delay
最有效的设置,因此建议将此值作为优化特定工作负载时使用的起点。虽然调整 commit_delay
在 WAL 存储在高延迟旋转磁盘上时特别有用,但即使在存储介质具有非常快的同步时间(例如固态驱动器或具有电池备份写入缓存的 RAID 阵列)的情况下,好处也可能很显著;但这必须针对代表性工作负载进行测试。在这种情况下,应使用更高的 commit_siblings
值,而在较高延迟介质上,较小的 commit_siblings
值通常会有所帮助。请注意,设置过高的 commit_delay
值可能会导致事务延迟增加,从而导致总事务吞吐量下降。
当 commit_delay
设置为零(默认值)时,仍然可能发生某种形式的组提交,但每个组只包含在先前刷新操作(如果有)正在发生的窗口中到达需要刷新其提交记录的点的会话。在更高的客户端数量下,往往会发生一种 “过道效应”,因此即使 commit_delay
为零,组提交的效果也会变得显著,因此显式设置 commit_delay
往往帮助不大。只有在 (1) 存在一些并发提交的事务,以及 (2) 吞吐量在一定程度上受到提交速率限制时,设置 commit_delay
才能有所帮助;但在高旋转延迟情况下,此设置可以在只有两个客户端的情况下有效地提高事务吞吐量(即一个提交客户端和一个兄弟事务)。
wal_sync_method 参数决定了 PostgreSQL 将如何要求内核将WAL更新强制写入磁盘。除了 fsync_writethrough
之外,所有选项在可靠性方面都应该相同,fsync_writethrough
有时会强制刷新磁盘缓存,即使其他选项没有这样做。但是,哪种选项最快,与平台密切相关。您可以使用 pg_test_fsync 程序测试不同选项的速度。请注意,如果已关闭 fsync
,则此参数无关紧要。
启用 wal_debug 配置参数(前提是 PostgreSQL 已编译为支持它)将导致每个 XLogInsertRecord
和 XLogFlush
WAL调用被记录到服务器日志中。将来,此选项可能会被更通用的机制取代。
有两个内部函数用于将 WAL 数据写入磁盘:XLogWrite
和 issue_xlog_fsync
。当 track_wal_io_timing 启用时,XLogWrite
写入 WAL 数据和 issue_xlog_fsync
将 WAL 数据同步到磁盘的总时间分别计为 wal_write_time
和 wal_sync_time
,位于 pg_stat_wal 中。通常情况下,XLogInsertRecord
(当 WAL 缓冲区中没有新记录的空间时)、XLogFlush
和 WAL 写入器调用 XLogWrite
,以将 WAL 缓冲区写入磁盘并调用 issue_xlog_fsync
。通常情况下,XLogWrite
调用 issue_xlog_fsync
,以将 WAL 文件同步到磁盘。如果 wal_sync_method
是 open_datasync
或 open_sync
,则 XLogWrite
中的写入操作保证将写入的 WAL 数据同步到磁盘,而 issue_xlog_fsync
则不执行任何操作。如果 wal_sync_method
是 fdatasync
、fsync
或 fsync_writethrough
,则写入操作将 WAL 缓冲区移动到内核缓存,而 issue_xlog_fsync
则将它们同步到磁盘。无论 track_wal_io_timing
的设置如何,XLogWrite
写入和 issue_xlog_fsync
将 WAL 数据同步到磁盘的次数也分别计为 wal_write
和 wal_sync
,位于 pg_stat_wal
中。
recovery_prefetch 参数可用于减少恢复期间的 I/O 等待时间,方法是指示内核启动对磁盘块的读取,这些磁盘块很快就会被需要,但目前不在 PostgreSQL 的缓冲池中。 maintenance_io_concurrency 和 wal_decode_buffer_size 设置分别限制预取并发性和距离。默认情况下,它设置为 try
,这将在有 posix_fadvise
可用的系统上启用此功能。
如果您在文档中看到任何不正确的内容、与您对特定功能的体验不符或需要进一步澄清,请使用 此表格 报告文档问题。