受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发版本: devel

Chapter 61. 通用 WAL 记录

虽然所有内置的、会生成 WAL 记录的模块都有各自的 WAL 记录类型,但也有一种通用 WAL 记录类型,可以用通用方式描述对页面的更改。这对于提供自定义访问方法的扩展很有用,因为它们无法注册自己的 WAL 重做例程。

用于构造通用 WAL 记录的 API 定义在access/generic_xlog.h中,并在access/transam/generic_xlog.c中实现。

要使用通用 WAL 记录机制执行一次需要写入 WAL 的数据更新,请遵循以下步骤:

  1. state = GenericXLogStart(relation) — 开始为给定关系构造一条通用 WAL 记录。

  2. page = GenericXLogRegisterBuffer(state, buffer, flags) — 注册一个将在当前通用 WAL 记录中被修改的缓冲区。该函数返回一个指向该缓冲区页面临时副本的指针,应在该副本上进行修改(不要直接修改缓冲区内容)。第三个参数是适用于该操作的标志掩码。目前唯一这样的标志是GENERIC_XLOG_FULL_IMAGE,表示应在 WAL 记录中包含整页镜像,而不是增量更新。通常在页面是新的,或者已经被完全重写时,会设置该标志。如果该操作需要修改多个页面,则可以重复调用GenericXLogRegisterBuffer

  3. 对上一步得到的页面镜像施加修改。

  4. GenericXLogFinish(state) — 将更改应用到缓冲区,并发出通用 WAL 记录。

在上述任意步骤之间,都可以调用GenericXLogAbort(state)取消 WAL 记录的构造。这会丢弃对页面镜像副本所做的全部更改。

在使用通用 WAL 记录功能时请注意以下几点:

  • 不允许直接修改缓冲区!所有修改都必须在通过GenericXLogRegisterBuffer()取得的副本上完成。换句话说,生成通用 WAL 记录的代码绝不应自行调用BufferGetPage()。不过,在适当的时机对缓冲区执行 pin/unpin 和 lock/unlock,仍然是调用者的责任。对于每个目标缓冲区,从调用GenericXLogRegisterBuffer()之前开始直到GenericXLogFinish()之后,都必须持有排他锁。

  • 注册缓冲区(步骤 2)和修改页面镜像(步骤 3)可以自由交错进行,也就是说,这两个步骤可以按任意顺序重复。请记住,注册缓冲区的顺序应当与重放时获取其锁的顺序一致。

  • 一个通用 WAL 记录最多能注册MAX_GENERIC_XLOG_PAGES个缓冲区。如果超过这个限制,就会抛出错误。

  • 通用 WAL 假定待修改的页面具有标准布局,尤其是假定pd_lowerpd_upper之间没有有用数据。

  • 由于修改的是缓冲区页面的副本,GenericXLogStart()不会开启临界区。因此,在GenericXLogStart()GenericXLogFinish()之间,可以安全地进行内存分配、抛出错误等操作。唯一真正的临界区位于GenericXLogFinish()内部。此外,也不必担心在错误退出时调用GenericXLogAbort()

  • GenericXLogFinish()会负责将缓冲区标记为脏并设置它们的 LSN。你不需要显式执行这些操作。

  • 对于不记录日志的关系,其他行为都完全相同,只是不会真正发出 WAL 记录。因此,通常不需要专门对不记录日志的关系做任何显式检查。

  • 通用 WAL 的重做函数会按照缓冲区注册的顺序获取这些缓冲区上的排他锁。在重做完所有更改后,这些锁也会按照同样的顺序释放。

  • 如果某个已注册缓冲区未指定GENERIC_XLOG_FULL_IMAGE,通用 WAL 记录中包含的就是旧页面镜像与新页面镜像之间的差异。该差异基于逐字节比较。对于在页面内移动数据的情况,这种表示方式并不十分紧凑,未来可能会改进。

提交更正

如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。