30.5. WAL 内部机制 #
WAL 会自动启用;管理员除了确保满足 WAL 文件的磁盘空间需求,并完成任何必要调优之外,不需要再做其他操作(见第 30.4 节)。
每写入一条新记录,WAL 记录就会追加到
WAL 文件中。插入位置由日志序列号(LSN)描述,它是 WAL 内的一个字节偏移量,会随着每条新记录的写入而单调递增。LSN 值以数据类型
pg_lsn 返回。通过比较两个值,可以计算它们之间相隔的 WAL 数据量,因此它们可用于衡量复制和恢复的进度。
WAL 文件保存在数据目录下的 pg_wal 目录中,由一组段文件组成,每个段通常为 16 MB(不过可在构建服务器时通过修改
--with-wal-segsize configure 选项来改变大小)。每个段又分为若干页面,通常每页 8 kB(该大小可通过 configure 选项 --with-wal-blocksize 更改)。WAL 记录头定义在
access/xlogrecord.h 中;记录内容则取决于被记录的事件类型。段文件以不断递增的数字命名,从
000000010000000000000001 开始。这些编号不会回绕,但要把可用的编号耗尽还需要非常、非常长的时间。
如果能将 WAL 放在与主数据库文件不同的磁盘上,会更有利。这可以通过把
pg_wal 目录移动到其他位置(当然要在服务器关闭时进行),然后在主数据目录中的原位置创建一个指向新位置的符号链接来实现。
WAL 的目标是确保数据库记录在被修改之前,日志已经先被写出;但如果磁盘驱动器实际上只是缓存了数据、尚未将其存储到磁盘上,却向内核谎报写入成功,这一目标就会被破坏。在这种情况下,断电可能导致不可恢复的数据损坏。管理员应尽量确保保存 PostgreSQL WAL 文件的磁盘不会做出这种虚假报告。(见第 30.1 节。)
在完成检查点并刷写 WAL 之后,该检查点的位置会保存在文件
pg_control 中。因此,在恢复开始时,服务器会先读取
pg_control,再读取检查点记录;然后从检查点记录给出的
WAL 位置向前扫描并执行 REDO。由于检查点之后对数据页的第一次修改,会把整页内容保存在 WAL 中(假设没有禁用 full_page_writes),因而自该检查点以来被修改的所有页面都会恢复到一致状态。
为处理 pg_control 损坏的情况,我们本应支持按相反顺序扫描现有 WAL 段 — 也就是从新到旧 —,以便找到最新检查点。这一功能尚未实现。pg_control 足够小(不足一个磁盘页),因此不会受部分写问题影响;到目前为止,也没有仅仅因为无法读取
pg_control 本身而导致数据库故障的报告。所以,尽管理论上它是个薄弱环节,pg_control 在实践中似乎不是问题。