Logging

在发生故障(例如停电、操作系统错误或数据库服务器崩溃)时,RAM 中的所有内容都将丢失;只有写入磁盘的数据会保留下来。要在故障后启动服务器,您必须恢复数据一致性。如果磁盘本身已损坏,则必须通过备份恢复来解决相同的问题。

理论上,您可以始终保持磁盘上的数据一致性。但实际上,这意味着服务器必须不断地将随机页面写入磁盘(尽管顺序写入成本更低),并且此类写入的顺序必须保证在任何特定时刻都不会损害一致性(这很难实现,尤其是当您处理复杂的索引结构时)。

与大多数数据库系统一样,PostgreSQL 采用了一种不同的方法。

服务器运行时,部分当前数据仅存在于 RAM 中,其写入永久存储的操作被推迟。因此,服务器运行时存储在磁盘上的数据始终是不一致的,因为页面从不会一次性全部刷新。但是,RAM 中发生的每个更改(例如在缓冲区缓存中执行的页面更新)都会被记录下来:PostgreSQL 会创建一个日志条目,其中包含在需要时重复此操作所需的所有基本信息。

页面修改相关的日志条目必须先于修改后的页面本身写入磁盘。这就是日志名称的由来:预写式日志(write-ahead log),简称 WAL。这项要求保证了在发生故障时,PostgreSQL 可以从磁盘读取 WAL 条目并重放它们,以重复那些已完成但结果仍在 RAM 中且在崩溃前未写入磁盘的操作。

保留预写式日志通常比将随机页面写入磁盘更高效。WAL 条目构成一个连续的数据流,即使是硬盘驱动器 (HDD) 也能很好地处理。此外,WAL 条目通常比页面大小更小。

为了在发生故障时避免数据不一致,所有可能破坏数据一致性的操作都需要记录下来。具体来说,以下操作会记录在 预写式日志 (WAL) 中:

  • WAL 记录的操作
  1. 缓冲区缓存中的页面修改:由于写入是延迟的,这些修改需要记录下来以备恢复。
  2. 事务提交和回滚:事务状态的变化发生在 CLOG 缓冲区中,不会立即写入磁盘,因此需要记录。
  3. 文件操作:当添加或删除表时,文件和目录的创建与删除等操作必须与数据更改同步,所以也要记录。
  • WAL 不记录的操作
  1. unlogged表相关操作
  2. 临时表上的操作:由于临时表的生命周期仅限于创建它们的会话,所以它们的操作不会被记录。

    在 PostgreSQL 10 之前,哈希索引的操作也不会被记录。它们的主要目的是将哈希函数与不同的数据类型匹配。

除了用于崩溃恢复之外,WAL 还可以用于从备份进行时间点恢复以及数据复制。

参考书目

  1. Egor Rogov, PostgreSQL 14 Internals, https://postgrespro.com/community/books/internals