29.5. WAL内部

WAL是自动被启用的。除了确保满足WAL日志存放所需要的磁盘空间以及一些必要的调优外(参阅第 29.4 节),管理员无需执行任何操作。

当每个新记录被写入时,WAL记录被追加到WAL日志中。 插入位置由日志序列号(LSN)描述,该日志序列号是日志中的字节偏移量, 随每个新记录单调递增。LSN值作为数据类型 pg_lsn返回。 值可以进行比较以计算分离它们的WAL数据量,因此它们用于衡量复制和恢复的进度。

WAL日志被存放在数据目录的pg_wal目录里,它是作为一个文件段的集合存储的,通常每个段16MB大小(不过这个大小可以通过initdb配置选项--with-wal-segsize来修改)。每个段分割成多个页,通常每个页为8K(该尺寸可以通过--with-wal-blocksize配置选项来修改)。日志记录头部在access/xlogrecord.h里描述;日志内容取决于它记录的事件类型。段文件的名字是不断增长的数字,从000000010000000000000001开始。目前这些数字不能回卷,不过要把所有可用的数字都用光也需要非常非常长的时间。

日志被放置在和主数据库文件不同的另外一个磁盘上会比较好。你可以通过把pg_wal目录移动到另外一个位置(当然在此期间服务器应当被关闭),然后在原来的位置上创建一个指向新位置的符号链接来实现重定位日志。

WAL的目的是确保在数据库记录被修改之前先写了日志,但是这可能会被那些谎称向内核写成功的破坏, 这时候它们实际上只是缓冲了数据而并未把数据存储到磁盘上。 这种情况下的电源失效仍然可能导致不可恢复的数据崩溃。 管理员应该确保保存PostgreSQLWAL日志文件的磁盘不会做这种谎报(参见第 29.1 节)。

在完成一个检查点并且刷写了日志文件之后,检查点的位置被保存在文件pg_control里。因此在恢复的开始, 服务器首先读取pg_control,然后读取检查点记录; 接着它通过从检查点记录里标识的日志位置开始向前扫描执行 REDO操作。 因为数据页的所有内容都保存在检查点之后的第一个页面修改的日志里(假设full_page_writes没有被禁用), 所以自检查点以来的所有变化的页都将被恢复到一个一致的状态。

为了处理pg_control被损坏的情况, 我们应该支持对于现有日志段反向扫描的功能 — 从最新到最老 — 这样才能找到最后的检查点。但这些目前还没有被实现。pg_control很小(比一个磁盘页小),因此它不会出现页断裂问题, 并且到目前为止还没有发现仅仅由于无法读取pg_control本身导致数据库失败的报告。 因此,尽管这在理论上是一个薄弱环节,但是pg_control看起来似乎并不是实际会发生的问题。