↑↓ 选择 ↵ 打开 ⌫ 改范围 完整检索页

pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。

文档 / SQL 命令 / 其它对象

SQL COMMAND · 其它对象

WAIT

等待 WAL 达到目标 LSN

WAIT其它对象引入 19于 20 移除0 次语法变更

动词
WAIT
对象
—
引入版本
19
状态
于 20 移除
语法变更次数
0
手册小节数
5

本站手册 · 19 beta 4官方文档 ↗

版本轨迹

PostgreSQL 19 新增此命令。

PostgreSQL 19 beta 4 尚未正式发布;本页来自对应手册快照,内容仍可能变化。

语法铁道图 PostgreSQL 19

沿轨道从左向右阅读,分岔表示选择,绕行表示可选,回环表示重复。方框为参数,点击带下划线的参数可展开子规则。

WAIT FOR LSN ' lsn ' WITH ( option , )
option
MODE ' mode ' TIMEOUT ' timeout ' NO_THROW
mode
standby_replay standby_write standby_flush primary_flush

语法概要

WAIT FOR LSN 'lsn'
    [ WITH ( option [, ...] ) ]

其中 option 可以是:

    MODE 'mode'
    TIMEOUT 'timeout'
    NO_THROW

而 mode 可以是:

    standby_replay | standby_write | standby_flush | primary_flush

PostgreSQL 19 beta 4 手册 · 查看完整参考页

描述

等待直到根据指定的 mode 达到指定的 lsn,该模式决定等待 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 更快,但持久性保证更弱,因为数据可能仍在操作系统缓冲区中。备库上已有的 WAL(来自基础备份、归档恢复或先前的流式传输)以及从主库新接收的 WAL 均可满足此条件。此模式只能在恢复期间使用。

  • standby_flush:等待包含该 LSN 的 WAL 在备库上刷盘。这提供了持久性保证,而无需等待 WAL 被应用。备库上已有的 WAL(来自基础备份、归档恢复或先前的流式传输)以及从主库新接收的 WAL 均可满足此条件。此模式只能在恢复期间使用。

  • primary_flush:等待包含该 LSN 的 WAL 在主库上刷盘。成功完成后,pg_current_wal_flush_lsn() 将返回大于或等于目标 LSN 的值。此模式只能在主库上使用(不能在恢复期间使用)。

TIMEOUT 'timeout'

在指定且 timeout 大于零时,命令会一直等待到 lsn 达到,或者直到指定的 timeout 到期为止。值为零(默认值)表示命令会无限期等待。

timeout 是以毫秒为单位的时长。也可以将其指定为一个字符串,其中包含数值及其后的时间单位(见第 19.1.1 节)。最大值为 2147483647 ms。

小数值会舍入到最接近的毫秒数。因此,注意半毫秒或更短的 timeout 会向下舍入为零,这意味着无限期等待。

NO_THROW

指定在超时或在主库上运行时不抛出错误。在这种情况下,可以从返回值中获取结果状态。

当 timeout 或 not in recovery 是应用预期并打算处理的结果时,可使用此选项,例如重试等待、报告复制延迟,或为后续操作选择另一台服务器。命令随后会将结果作为状态返回;应用必须检查该状态,才能认定已达到目标 LSN。如果应用必须在达到目标 LSN 后才能继续,则应省略此选项,这样等待未成功时就会通过错误停止正常执行。

返回状态也会使显式事务保持可用;如果不使用此选项,相应错误会要求先回滚事务,或回滚到保存点,才能发出后续命令。

此选项只改变 timeout 和 not in recovery 的报告方式。其他错误仍会抛出,包括无效输入(例如格式错误的 LSN 或无法识别的选项值),以及等待开始前检查的所有条件(例如在恢复期间请求 primary_flush,或在持有锁的同时等待备库 LSN)。此选项也不限制等待时长;要限制时长,应指定 TIMEOUT。

输出

success

此返回值表示已成功达到目标 lsn。

timeout

此返回值表示在达到目标 lsn 之前发生了超时。

not in recovery

此返回值表示数据库服务器不处于恢复状态。这可能意味着数据库服务器在接收到命令时并不处于恢复状态(即在主库上执行),也可能意味着它在达到目标 lsn 之前被提升。在提升的情况下,此状态表示发生了时间线变更,应用程序应重新评估目标 LSN 是否仍然适用。

备注

WAIT 必须作为顶层命令执行,不能从函数、过程或 DO 块中执行。当前事务持有快照时也不能执行。WAIT 本身不获取快照,因此可以在 REPEATABLE READ 或 SERIALIZABLE 事务中第一条获取快照的语句之前运行,但不能在该语句之后运行;无论处于何种隔离级别,如果游标或导出的快照使事务持有快照,也不能运行。此处持有的快照可能延迟重放,而 standby_replay 会等待重放,其他备库模式最终也可能需要等待重放。这也是 WAIT 被实现为命令而不是函数或过程的原因,因为函数和过程执行时会持有快照。

恢复进行期间,如果会话已经持有锁,且尚未达到目标 lsn,则会拒绝 standby_replay(默认)、standby_write 或 standby_flush 模式的等待。这样的锁可能使启动进程直接或通过另一个会话等待当前会话,而当前会话又在等待启动进程推进恢复。这个循环在当前会话一侧不涉及锁等待,因而死锁检测无法发现,也没有机制可以打破它。如果已经达到目标,等待会立即返回,因此始终允许。

应在事务块之外发出 WAIT FOR,或将其作为事务块的第一条语句,在运行任何会获取锁的操作之前执行。这也是下文示例所示读己之写模式的自然顺序:先等待目标 lsn,再运行必须看到相应更改的查询。注意,在 READ COMMITTED 下,即使先前语句的快照已经释放,它获取的锁仍会保留。因此,即使隔离级别允许等待,在这类语句之后执行等待仍会被拒绝。

此限制也适用于 standby_write 和 standby_flush,尽管流复制可以在不依赖启动进程的情况下推进这两个位置。这两个位置都不会落后于重放位置,因此当 walreceiver 未处于活动状态时,重放可能是推进它们的唯一途径。如果所持有的锁阻塞了重放,会话就在等待重放推进,而重放又在等待会话释放锁。在流复制下,只有 WAL 持续到达时,这些位置才会独立推进。如果接收在达到目标之前停止,被阻塞的启动进程就无法重新启动 walreceiver。它也无法重放推进重启点和回收 WAL 所需的较新检查点记录,因此 pg_wal 可能被填满,导致接收在达到目标之前停止。所以,即使等待开始时流复制处于活动状态,此限制也仍然适用。

WAIT 会根据指定的 mode 一直等待直到达到指定的 lsn。standby_replay 模式会等待该 LSN 被重放(应用到数据库),这有助于在使用异步副本读取和主库写入时实现读己之写一致性,前提是目标 LSN 位于主库上相关写事务的 COMMIT 记录末尾或其后。standby_flush 模式会等待 WAL 在副本上刷入持久存储,或者等待备库上已有的 WAL 已被重放。standby_write 模式会等待 WAL 被写入操作系统,或者已经重放;对于新接收的 WAL,这比刷盘更快,但持久性保证更弱。primary_flush 模式会等待 WAL 在主库上刷盘。在所有情况下,最后一次修改的 LSN 应当存储在客户端应用程序一侧或连接池一侧。

备库模式(standby_replay、standby_write、standby_flush)只能在恢复期间使用,而 primary_flush 只能在主库上使用。对当前服务器状态使用错误的模式会导致错误。如果在使用备库模式等待期间备库被提升,命令将返回 not in recovery(或者在未指定 NO_THROW 时抛出错误)。提升会创建新的时间线,而正在等待的 LSN 可能指向旧时间线中的 WAL。

WAIT只比较LSN数值;它并不知道 WAL 记录属于哪条时间线。当备库跨上游时间线切换继续恢复时,这一点很重要—例如,级联备库的上游被提升。在这种情况下,只要所选等待模式使用的位置达到或超过该LSN数值,WAIT就会返回success,而不考虑该 LSN属于哪条时间线。需要确认目标指向预期时间线的应用程序必须自行验证时间线。

在备库服务器上,WAIT 会话可能会被恢复冲突中断。某些恢复冲突是不可避免的:例如,重放一个表空间删除操作时,无论后端正在执行什么,都会通过终止所有后端来解决冲突。在备库上使用 WAIT 的应用程序应准备好处理此类中断,例如通过重试命令或回退到其他机制。

示例

可以使用 WAIT 命令等待 pg_lsn 值。例如,应用程序可以更新 movie 表,并获取位于相关写事务的 COMMIT 记录末尾或其后的 lsn。在这里展示的默认自动提交模式下,UPDATE 会在后续的 SELECT 之前提交。由于 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/0306EE20
(1 row)

然后,应用程序可以使用提交之后从主库获得的 lsn 运行 WAIT。之后,主库上所做的更改应当保证在副本上可见。

postgres=# WAIT FOR LSN '0/0306EE20';
 status
---------
 success
(1 row)
postgres=# SELECT * FROM movie WHERE genre = 'Drama';
 genre
-------
(0 rows)

等待刷盘(数据在副本上持久化):

postgres=# WAIT FOR LSN '0/0306EE20' WITH (MODE 'standby_flush');
 status
---------
 success
(1 row)

带超时的等待写入:

postgres=# WAIT FOR LSN '0/0306EE20' WITH (MODE 'standby_write', TIMEOUT '100ms', NO_THROW);
 status
---------
 success
(1 row)

等待主库刷盘:

postgres=# WAIT FOR LSN '0/0306EE20' WITH (MODE 'primary_flush');
 status
---------
 success
(1 row)

如果在超时之前未达到目标 LSN,则会抛出错误:

postgres=# WAIT FOR LSN '0/0306EE20' WITH (TIMEOUT '0.1s');
ERROR:  timed out while waiting for target LSN 0/0306EE20 to be replayed; current standby_replay LSN 0/0306EA60

同一个示例使用带 NO_THROW 选项的 WAIT:

postgres=# WAIT FOR LSN '0/0306EE20' WITH (TIMEOUT '100ms', NO_THROW);
 status
---------
 timeout
(1 row)

语法演化

相邻大版本之间的差异,新的在前。版本号链接到对应快照。

  1. PostgreSQL 20← 19移除此命令

  2. PostgreSQL 19← 18新增此命令

同组命令

命令动词对象版本变动最近变更
OTHER OBJECTS其它对象4 条↑
COMMENTCOMMENT— 111 次
定义或修改对象的注释现存
REPACKREPACK— —
重写表以回收磁盘空间现存
WAITWAIT— —
等待 WAL 达到目标 LSN于 20 移除
ALTER LARGE OBJECTALTERLARGE OBJECT 141 次
更改一个大对象的定义现存

本站手册收录版本:19 beta 4

内容来自本站 PostgreSQL 手册译文,最后同步 2026-09-27。

译文问题请提交到 pgsty/pgdoc;本站展示或功能问题请提交到 pgsty/pgweb。