2025年9月25日: PostgreSQL 18 发布!
支持的版本: 当前 (18) / 17 / 16 / 15 / 14 / 13
开发版本: devel
不支持的版本: 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3 / 7.2 / 7.1

28.5. WAL配置 #

有几个WAL-相关的配置参数会影响数据库的性能。本节将解释它们的使用方法。有关设置服务器配置参数的一般信息,请参阅第 19 章

检查点 是事务序列中的一些点,在这些点上,保证堆和索引数据文件已更新为在检查点之前写入的所有信息。在检查点时,所有脏数据页都会被刷新到磁盘,并在 WAL 文件中写入一个特殊的检查点记录。(更改记录以前被刷新到WAL文件。)如果发生崩溃,崩溃恢复过程会查看最新的检查点记录,以确定 WAL (称为重做记录) 中应从何处开始执行 REDO 操作。在重做记录之前的任何对数据文件的更改都保证已在磁盘上。因此,在检查点之后,包含重做记录的 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 允许您在配置的字节数之后强制将检查点写入的 OS 页面刷新到磁盘。否则,这些页面可能会保留在 OS 的页面缓存中,当 fsync 在检查点结束时发出时,会导致停顿。此设置通常有助于减少事务延迟,但它也可能对性能产生不利影响;特别是对于大于 shared_buffers 但小于 OS 页面缓存的工作负载。

pg_wal 目录中的 WAL 段文件的数量取决于 min_wal_sizemax_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_commandarchive_library 频繁失败,旧 WAL 文件将在 pg_wal 中累积,直到情况得到解决。缓慢或失败的备用服务器使用复制槽会产生相同的效果(请参阅第 26.2.6 节)。同样,如果启用了WAL 摘要,旧段将在摘要之前保留。

在归档恢复或备用模式下,服务器会定期执行重启点,这与正常操作中的检查点类似:服务器将所有状态强制写入磁盘,更新 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 计数器可能会显示峰值增长。这是因为由于 WAL 消耗增加而创建新重启点的请求无法执行,因为自上次重启点以来安全检查点记录尚未在备用服务器上重放。此行为是正常的,不会导致系统资源消耗增加。在重启点相关的计数器中,只有 restartpoints_done 计数器指示已花费了明显的系统资源。

有两个常用的内部WAL函数:XLogInsertRecordXLogFlushXLogInsertRecord 用于将新记录放入共享内存中的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 个其他会话当前处于活动事务中,则不会发生休眠;这可以避免在不太可能立即有其他会话提交时休眠。请注意,在某些平台上,休眠请求的分辨率为十毫秒,因此任何非零的 commit_delay 设置(介于 1 到 10000 微秒之间)都会产生相同的效果。另请注意,在某些平台上,休眠操作可能比参数请求的时间稍长。

由于 commit_delay 的目的是允许每次刷新操作的成本分摊到并发提交的事务上(可能以牺牲事务延迟为代价),因此有必要在合理地选择设置之前量化该成本。该成本越高,commit_delay 在提高事务吞吐量方面预期会更有效,但也有一个上限。可以使用 pg_test_fsync 程序来测量单次 WAL 刷新操作平均需要多少微秒。通常,报告的程序在一小时 8kB 写操作后刷新的平均时间的一半的延迟值是 commit_delay 的最有效设置,因此建议以此值作为优化特定工作负载的起点。虽然对 commit_delay 进行调优在 WAL 存储在高延迟旋转磁盘上时特别有用,但即使在同步时间非常快的存储介质(如固态驱动器或带有电池备份写缓存的 RAID 阵列)上,其优点也可能非常显著;但这绝对应该根据代表性的工作负载进行测试。在这种情况下,应该使用更高的 commit_siblings 值,而在高延迟介质上,较小的 commit_siblings 值通常很有用。请注意,commit_delay 设置过高很有可能导致事务延迟过大,从而导致总事务吞吐量下降。

commit_delay 设置为零(默认值)时,仍然可能发生一种形式的组提交,但每个组将仅包含在先前刷新操作(如果存在)正在发生的窗口期间达到需要刷新其提交记录的会话。在更高的客户端数量下,会倾向于出现一种“通道效应”(gangway effect),因此即使 commit_delay 为零,组提交的效果也会变得显著,从而显式设置 commit_delay 的效果往往较小。设置 commit_delay 只能在 (1) 存在一些并发提交的事务,并且 (2) 吞吐量在一定程度上受到提交速率的限制时才有帮助;但对于高旋转延迟,此设置可以有效地提高事务吞吐量,即使只有两个客户端(即,一个提交客户端和一个兄弟事务)。

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

启用 wal_debug 配置参数(前提是 PostgreSQL 已编译支持它),将导致每次 XLogInsertRecordXLogFlushWAL调用都被记录到服务器日志中。此选项将来可能会被更通用的机制取代。

有两个内部函数用于将 WAL 数据写入磁盘:XLogWriteissue_xlog_fsync。当启用 track_wal_io_timing 时,XLogWrite 写入和 issue_xlog_fsync 同步 WAL 数据到磁盘的总时间分别作为 write_timefsync_timepg_stat_io 中为 object wal 计数。XLogWrite 通常由 XLogInsertRecord(当 WAL 缓冲区没有新记录的空间时)、XLogFlush 和 WAL 写入器调用,用于将 WAL 缓冲区写入磁盘并调用 issue_xlog_fsyncissue_xlog_fsync 通常由 XLogWrite 调用,用于将 WAL 文件同步到磁盘。如果 wal_sync_methodopen_datasyncopen_sync,那么 XLogWrite 中的写入操作保证将写入的 WAL 数据同步到磁盘,而 issue_xlog_fsync 则不执行任何操作。如果 wal_sync_methodfdatasyncfsyncfsync_writethrough,则写入操作会将 WAL 缓冲区移动到内核缓存,而 issue_xlog_fsync 将它们同步到磁盘。无论 track_wal_io_timing 的设置如何,XLogWrite 写入和 issue_xlog_fsync 同步 WAL 数据到磁盘的次数也分别作为 writesfsyncspg_stat_io 中为 object wal 计数。

recovery_prefetch 参数可以用于通过指示内核启动对即将需要但当前不在 PostgreSQL 缓冲区池中的磁盘块的读取,来减少恢复期间的 I/O 等待时间。maintenance_io_concurrencywal_decode_buffer_size 设置分别限制了预读并发度和距离。默认情况下,它设置为 try,这会在支持发出预读建议的系统上启用该功能。

提交更正

如果您在文档中看到任何不正确、与您对特定功能的体验不符或需要进一步澄清的内容,请使用 此表单 来报告文档问题。