WAIT FOR — 等待 WAL 达到目标 LSN
WAIT FOR LSN 'lsn' [ WITH (option[, ...] ) ] whereoptioncan be: MODE 'mode' TIMEOUT 'timeout' NO_THROW andmodecan be: standby_replay | standby_write | standby_flush | primary_flush
等待直到根据指定的 mode 达到指定的 lsn, 其中 mode 决定等待 WAL 被写入、刷盘还是回放。 如果没有指定 timeout,或者其值为零,则此命令会无限期等待 lsn。
超时时会发出错误,除非在 WITH 子句中指定了 NO_THROW。 对于备库模式(standby_replay、standby_write、 standby_flush),如果在达到 lsn 之前服务器被提升, 也会发出错误。如果指定了 NO_THROW,命令会返回一个状态字符串,而不是抛出错误。
可能的返回值是 success、timeout 和 not in recovery。
lsn #指定要等待的目标 LSN。
WITH ( option [, ...] ) #此子句指定等待操作的可选参数。支持以下参数:
MODE 'mode' #指定要等待的 LSN 处理类型。如果未指定,默认值为 standby_replay。有效模式如下:
standby_replay:等待该 LSN 在备库上被回放(应用到数据库)。 成功完成后,pg_last_wal_replay_lsn() 将返回大于或等于目标 LSN 的值。 此模式只能在恢复期间使用。
standby_write:等待包含该 LSN 的 WAL 从主库接收并写入备库磁盘, 但尚未刷盘。这比 standby_flush 更快,但持久性保证更弱,因为数据可能仍在操作系统缓冲区中。 成功完成后, pg_stat_wal_receiver 中的 written_lsn 列将显示大于或等于目标 LSN 的值。 此模式只能在恢复期间使用。
standby_flush:等待包含该 LSN 的 WAL 从主库接收并刷盘到备库磁盘。 这提供了持久性保证,而无需等待 WAL 被应用。成功完成后, pg_last_wal_receive_lsn() 将返回大于或等于目标 LSN 的值。 该值也可作为 pg_stat_wal_receiver 中的 flushed_lsn 列获取。此模式只能在恢复期间使用。
primary_flush:等待包含该 LSN 的 WAL 在主库上刷盘。 成功完成后,pg_current_wal_flush_lsn() 将返回大于或等于目标 LSN 的值。 此模式只能在主库上使用(不能在恢复期间使用)。
TIMEOUT 'timeout' #在指定且 timeout 大于零时,命令会一直等待到 lsn 达到,或者直到指定的 timeout 到期为止。
timeout 可以给定为整数毫秒数。 也可以给定为字符串字面量,表示整数毫秒数,或者带单位的数值 (见 Section 19.1.1)。
NO_THROW #指定在超时或在主库上运行时不抛出错误。在这种情况下,可以从返回值中获取结果状态。
WAIT FOR 必须作为顶层命令执行。 它不能在函数、过程或 DO 块中执行。 它还要求不存在活动或已注册的快照,因此不能在必须保持此类快照处于活动状态的上下文中使用, 包括隔离级别高于 READ COMMITTED 的事务。
WAIT FOR 会根据指定的 mode 一直等待直到达到指定的 lsn。standby_replay 模式会等待该 LSN 被回放(应用到数据库), 这有助于在使用异步副本读取和主库写入时实现读己之写一致性。 standby_flush 模式会等待 WAL 在副本上刷入持久存储,从而在不等待回放的情况下提供持久性保证。 standby_write 模式会等待 WAL 写入操作系统,这比刷盘更快,但持久性保证更弱。 primary_flush 模式会等待 WAL 在主库上刷盘。 在所有情况下,最后一次修改的 LSN 应当存储在客户端应用程序一侧或连接池一侧。
备库模式(standby_replay、standby_write、 standby_flush)只能在恢复期间使用,而 primary_flush 只能在主库上使用。对当前服务器状态使用错误的模式会导致错误。 如果在使用备库模式等待期间备库被提升,命令将返回 not in recovery(或者在未指定 NO_THROW 时抛出错误)。提升会创建新的时间线,而正在等待的 LSN 可能指向旧时间线中的 WAL。
在备库服务器上,WAIT FOR 会话可能会被恢复冲突中断。 某些恢复冲突是不可避免的:例如,回放一个表空间删除操作时,无论后端正在执行什么,都会通过终止所有后端来解决冲突。 在备库上使用 WAIT FOR 的应用程序应准备好处理此类中断,例如通过重试命令或回退到其他机制。
可以使用 WAIT FOR 命令等待 pg_lsn 值。 例如,应用程序可以更新 movie 表,并在刚刚完成更改后获取 lsn。 由于 synchronous_commit 可能设置为 off,此示例在主库上使用 pg_current_wal_insert_lsn 获取 lsn。
postgres=# UPDATE movie SET genre = 'Dramatic' WHERE genre = 'Drama'; UPDATE 100 postgres=# SELECT pg_current_wal_insert_lsn(); pg_current_wal_insert_lsn --------------------------- 0/306EE20 (1 row)
然后,应用程序可以使用从主库获得的 lsn 运行 WAIT FOR。 之后,主库上所做的更改应当保证在副本上可见。
postgres=# WAIT FOR LSN '0/306EE20'; status --------- success (1 row) postgres=# SELECT * FROM movie WHERE genre = 'Drama'; genre ------- (0 rows)
等待刷盘(数据在副本上持久化):
postgres=# WAIT FOR LSN '0/306EE20' WITH (MODE 'standby_flush'); status --------- success (1 row)
带超时的等待写入:
postgres=# WAIT FOR LSN '0/306EE20' WITH (MODE 'standby_write', TIMEOUT '100ms', NO_THROW); status --------- success (1 row)
等待主库刷盘:
postgres=# WAIT FOR LSN '0/306EE20' WITH (MODE 'primary_flush'); status --------- success (1 row)
如果在超时之前未达到目标 LSN,则会抛出错误:
postgres=# WAIT FOR LSN '0/306EE20' WITH (TIMEOUT '0.1s'); ERROR: timed out while waiting for target LSN 0/306EE20 to be replayed; current replay LSN 0/306EA60
同一个示例使用带 NO_THROW 选项的 WAIT FOR:
postgres=# WAIT FOR LSN '0/306EE20' WITH (TIMEOUT '100ms', NO_THROW); status --------- timeout (1 row)