有几个WAL相关的配置参数会影响数据库性能。本节将解释它们的使用。关于服务器配置参数的设置的一般信息请参考Chapter 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允许您强制将
检查点写入的操作系统页面在达到可配置字节数后刷新到磁盘。否则,这些页面可能
会保留在操作系统的页面缓存中,导致在检查点结束时执行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
中积累,直到问题解决。
使用复制槽的慢速或失败的备用服务器也会产生相同的效果(参见
Section 26.2.6)。同样,如果启用了
WAL 汇总,旧的段会被保留,直到它们被汇总。
在归档恢复或备用模式下,服务器会定期执行重启点,
这类似于正常操作中的检查点:服务器将其所有状态强制写入磁盘,更新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
被设置且系统非常繁忙时,将wal_buffers
设置得更高
将有助于在每次检查点之后的时间段内平滑响应时间。
commit_delay定义了一个组提交领导者进程在XLogFlush
中要求一个锁之后将会休眠的微秒数,而组提交追随者都排队等候在领导者之后。这样的延迟可以允许其它服务器进程把它们提交的记录追加到WAL缓冲区中,这样所有的这些记录将会被领导者的最终同步操作刷出。如果fsync被禁用或者当前处于活跃事务中的会话数少于commit_siblings,休眠将不会发生;这样就避免了在其它事务不会很快提交的情况下进行休眠。 请注意在某些平台上,休眠要求的单位是十毫秒,所以任何介于 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
被设置为0(默认值),仍然有可能出现组提交的形式,但是组中的成员只能是那些在前一个刷写操作发生过程窗口中需要刷写它们提交记录的会话。在较高的客户端数量时很可能发生“gangway effect”,因此即使commit_delay
为0,组提交的效果也很显著,并且显式地设置commit_delay
将会没有作用。设置commit_delay
只有在两种情况下有帮助:(1)有一些并发提交的事务,以及(2)吞吐量在某种程度上被提交率限制。但是在高旋转延迟的设备上,即使少到只有两个客户端,该设置也能有效提高事务吞吐量。
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数据到磁盘的总时间被分别计数为wal_write_time
和 wal_sync_time
,在pg_stat_wal中。
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
的系统上启用该功能。