有几个WAL相关的配置参数会影响数据库性能。本节解释它们的用法。有关设置服务器配置参数的常规信息,请参考 第 19 章。
检查点 是事务序列中的一些点,保证堆和索引数据文件已使用在该检查点之前写入的所有信息进行更新。在检查点时,所有脏数据页都会刷新到磁盘,并且会在 WAL 文件中写入一个特殊的检查点记录。(更改记录先前已刷新到WAL文件中。)如果发生崩溃,崩溃恢复过程会查看最新的检查点记录,以确定应从中开始 REDO 操作的 WAL 中的点(称为重做记录)。保证在该点之前对数据文件所做的任何更改都已在磁盘上。因此,在检查点之后,不再需要包含重做记录的 WAL 段之前的 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 允许您强制检查点写入的操作系统页面在配置数量的字节之后刷新到磁盘。否则,这些页面可能会保留在操作系统的页面缓存中,从而在检查点结束时发出 fsync
时导致停顿。此设置通常有助于减少事务延迟,但也会对性能产生不利影响;特别是对于大于 shared_buffers 但小于操作系统页面缓存的工作负载。
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 段文件。重启点的执行频率不能高于主服务器上的检查点,因为重启点只能在检查点记录处执行。重启点可以通过计划或外部请求触发。pg_stat_checkpointer
视图中的 restartpoints_timed
计数器统计的是第一种情况,而 restartpoints_req
计数器统计的是第二种情况。当达到检查点记录时,如果自上次执行重启点以来至少经过了 checkpoint_timeout 秒,或者上次尝试执行重启点失败,则会按计划触发重启点。在后一种情况下,将在 15 秒后计划下一次重启点。由于类似于检查点的原因(主要是当 WAL 大小即将超过 max_wal_size 时),请求会触发重启点。但是,由于重启点的执行时间受到限制,在恢复期间 max_wal_size
经常会被超过,最多会超出相当于一个检查点周期大小的 WAL。(max_wal_size
本身就不是一个硬性限制,因此您应始终留有足够的余量,以避免磁盘空间不足。)pg_stat_checkpointer
视图中的 restartpoints_done
计数器统计的是实际已执行的重启点。
在某些情况下,当主服务器上的 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 并且系统非常繁忙时,将 wal_buffers
设置得更高将有助于平滑每次检查点后立即出现的响应时间。
commit_delay 参数定义了在 XLogFlush
中获取锁后,组提交的领导者进程将休眠多少微秒,而组提交的追随者则在领导者后面排队。此延迟允许其他服务器进程将其提交记录添加到 WAL 缓冲区,以便所有这些记录都将由领导者的最终同步操作刷新。如果未启用 fsync,或者当前活动事务中的其他会话少于 commit_siblings,则不会发生休眠;这可以避免在不太可能很快有其他会话提交时休眠。请注意,在某些平台上,休眠请求的分辨率为十毫秒,因此任何介于 1 到 10000 微秒之间的非零 commit_delay
设置都将具有相同的效果。另请注意,在某些平台上,休眠操作可能比参数请求的时间稍长。
由于 commit_delay
的目的是允许将每次刷新操作的成本分摊到并发提交的事务上(可能会以牺牲事务延迟为代价),因此在智能地选择设置之前,有必要量化该成本。成本越高,commit_delay
预计在提高事务吞吐量方面的效果就越明显,直到达到某个临界点。pg_test_fsync 程序可用于测量单个 WAL 刷新操作的平均时间(以微秒为单位)。该程序报告在单个 8kB 写入操作后刷新所需的平均时间的一半通常是 commit_delay
最有效的设置,因此建议将此值用作针对特定工作负载进行优化时的起点。虽然在将 WAL 存储在具有高延迟的旋转磁盘上时,调整 commit_delay
特别有用,但在具有非常快的同步时间的存储介质(例如固态硬盘或具有电池备份写入缓存的 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
写入和 issue_xlog_fsync
将 WAL 数据同步到磁盘的总时间将分别在 pg_stat_wal 中计为 wal_write_time
和 wal_sync_time
。XLogWrite
通常由 XLogInsertRecord
(当 WAL 缓冲区中没有空间存放新记录时)、XLogFlush
和 WAL 写入器调用,以将 WAL 缓冲区写入磁盘并调用 issue_xlog_fsync
。issue_xlog_fsync
通常由 XLogWrite
调用,以将 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 数据同步到磁盘的次数也会分别在 pg_stat_wal
中计为 wal_write
和 wal_sync
。
可以使用 recovery_prefetch 参数来减少恢复期间的 I/O 等待时间,方法是指示内核启动对即将需要但当前不在 PostgreSQL 的缓冲区池中的磁盘块的读取。maintenance_io_concurrency 和 wal_decode_buffer_size 设置分别限制预取的并发性和距离。默认情况下,它设置为 try
,这会在 posix_fadvise
可用的系统上启用该功能。
如果您在文档中发现任何不正确的内容,与特定功能的体验不符或需要进一步澄清的地方,请使用此表单报告文档问题。