pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
有若干与 WAL 相关的配置参数会影响数据库性能。本节解释它们 的用法。有关设置服务器配置参数的一般信息,请参见 第 18 章。
检查点是事务序列中的一些时间点,在这些点可以保证堆和索引数据文件已经更新了检查点之前写入的所有信息。在检查点时刻,所有脏数据页都被刷写到磁盘,并向日志文件写入一条特殊的检查点记录。(这些更改此前已被刷写到WAL文件中。)一旦发生崩溃,崩溃恢复过程会查看最新的检查点记录,以确定日志中开始 REDO 操作的位置(称为重做记录)。在此之前对数据文件所做的更改都保证已在磁盘上。因此,检查点之后,包含重做记录的日志段之前的日志段不再需要,可以循环使用或删除。(在进行WAL归档时,日志段在循环使用或删除之前必须先归档。)
检查点要求把所有脏数据页刷写到磁盘,这可能造成显著的 I/O 负载。因此,检查点活动是被限流的:I/O 从检查点开始时启动,并在下一个检查点开始之前完成;这样可以把检查点期间的性能退化降到最低。
服务器的后台写进程会自动定期执行检查点。每隔checkpoint_segments个日志段,或每隔checkpoint_timeout秒(以先到者为准)就会创建一个检查点。默认设置分别是 3 个段和 300 秒(5 分钟)。也可以使用 SQL 命令CHECKPOINT强制创建检查点。
减小checkpoint_segments和/或checkpoint_timeout会使检查点更频繁地发生。这可以让崩溃后的恢复更快(因为需要重做的工作更少)。不过,必须与更频繁地刷写脏数据页所增加的代价相权衡。如果启用了full_page_writes(默认如此),还有另一个因素需要考虑。为保证数据页一致性,每个检查点之后对数据页的第一次修改会把整页内容记入日志。在这种情况下,更小的检查点间隔会增加写入 WAL 日志的数据量,部分抵消了使用更小间隔的目的,而且无论如何都会导致更多磁盘 I/O。
检查点的代价相当高:首先它要求写出所有当前脏缓冲区,其次如上所述它还会导致后续额外的 WAL 流量。因此,把检查点参数设置得足够高、使检查点不至于过于频繁是明智的。作为对检查点参数的简单检查,可以设置checkpoint_warning参数。如果检查点发生的间隔小于checkpoint_warning秒,服务器日志中就会输出一条消息,建议增大checkpoint_segments。偶尔出现这样的消息不必惊慌,但如果它频繁出现,就应当增大检查点控制参数。如果没有把checkpoint_segments设置得足够高,大型COPY传输之类的批量操作可能导致出现多条这样的警告。
为了避免大量页面写出在短时间内淹没 I/O 系统,检查点期间对脏缓冲区的写出会被 分散到一段时间内。这段时间由 checkpoint_completion_target 控制,它表示为检查点间隔的一个分数。系统会调节 I/O 速率,使检查点在自检查点开始以来已消耗给定比例的 checkpoint_segments 个 WAL 段、或者已过去给定比例的 checkpoint_timeout 秒时完成,以较早者为准。使用默认值 0.5 时,可以预期 PostgreSQL 会在下一次检查点开始前的这段时间约过一半时完成每个检查点。 在正常运行时已非常接近最大 I/O 吞吐量的系统上,你可能希望增大 checkpoint_completion_target,以降低检查点带来的 I/O 负载。 这样做的缺点是,延长检查点会影响恢复时间,因为需要保留更多 WAL 段以备恢复时 使用。虽然 checkpoint_completion_target 可以设到 1.0,但最好 保持在该值以下(或许最多为 0.9),因为检查点除了写出脏缓冲区之外还包含其他活动。 设置为 1.0 很可能导致检查点不能按时完成,从而因为所需 WAL 段数量的意外波动而 损失性能。
WAL 段文件总是至少有一个,通常不会多于wal_keep_segments与(2 + checkpoint_completion_target) * checkpoint_segments + 1两者中较高的那个。 每个段文件通常是 16 MB(不过构建服务器时可以改变这个大小)。可以用它来估算WAL的空间需求。通常,当日志段文件不再需要时,它们会被循环使用(改名为编号序列中的后续段)。如果由于日志输出速率的短期峰值,段文件数超过了 3 * checkpoint_segments + 1,则不需要的段文件将被删除而不是循环使用,直到系统回到该限制之内。
在归档恢复或备库模式下,服务器会周期性地执行重启点,它类似于正常运行中的检查点:服务器把其所有状态强制写到磁盘,更新pg_control文件以指示已处理的 WAL 数据无须再次扫描,然后回收pg_xlog目录中的旧日志段文件。如果自上次重启点以来至少已重放一条检查点记录且已过去checkpoint_timeout秒,就会触发重启点。在备库模式下,如果自上次重启点以来已重放checkpoint_segments个日志段,也会触发重启点。由于重启点只处理上次重启点之后完成的写操作,重启点对于主库上的性能影响很小。
有两个常用的内部WAL函数:LogInsert和LogFlush。LogInsert用于把新记录放入共享内存中的WAL缓冲区。如果没有存放新记录的空间,LogInsert就不得不把(移动到内核缓存中)一些已写满的WAL缓冲区写出。这是不希望发生的,因为LogInsert在每次数据库底层修改(例如行插入)时都会被调用,而且此时受影响的数据页上持有排他锁,所以该操作需要尽可能快。更糟的是,写出WAL缓冲区还可能强制创建新的日志段,这需要更多时间。通常,WAL缓冲区应当由LogFlush请求来写出并刷盘,该请求大部分在事务提交时发出,以确保事务记录被刷到永久存储。在日志输出量很大的系统上,LogFlush请求可能不够频繁,无法避免LogInsert不得不进行写操作。在这样的系统上,应当通过修改配置参数wal_buffers来增加WAL缓冲区的数量。WAL缓冲区的默认数量是 8。增大这个值会相应增加共享内存的使用。当启用了full_page_writes且系统非常繁忙时,调高这个值有助于平滑每个检查点之后紧随时段的响应时间。
commit_delay参数定义服务器进程在用LogInsert把提交记录写入日志之后、执行LogFlush之前,将休眠多少微秒。这一延迟让其他服务器进程可以把它们的提交记录也加入日志,从而所有这些记录可以通过一次日志同步一起刷盘。如果未启用fsync,或者当前处于活动事务中的其他会话少于commit_siblings个,则不会发生休眠;这避免了在其他会话不太可能很快提交时进行休眠。注意,在大多数平台上,休眠请求的分辨率是十毫秒,因此任何介于 1 到 10000 微秒之间的非零commit_delay设置效果都相同。这些参数的理想值尚不明确;鼓励进行实验。
wal_sync_method参数决定PostgreSQL以何种方式请求内核把WAL更新强制写到磁盘。就可靠性而言,所有选项都相同,唯一例外是fsync_writethrough,会在其他选项不强制刷写磁盘缓存时强制进行刷写。不过,哪个选项最快与平台密切相关;可以用 PostgreSQL 源码树中的src/tools/fsync工具测试各选项的速度。注意,如果关闭了fsync,此参数就无关紧要了。
启用wal_debug配置参数(前提是PostgreSQL编译时包含了相应支持)会把每次LogInsert和LogFlushWAL调用记录到服务器日志中。此选项将来可能被更通用的机制取代。
译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。