{"Entry":{"collection":"sql","key":"wait","name":"WAIT","aliases":["wait"],"metadata":{"aliases":["wait"],"changed_in":[],"changes":[{"from":"18","purpose_changed":false,"renamed":null,"sections":{"added":[],"changed":[],"removed":[]},"status":"added","synopsis":null,"to":"19"},{"from":"19","purpose_changed":false,"renamed":null,"sections":{"added":["notes"],"changed":["parameters"],"removed":["other"]},"status":"changed","synopsis":null,"to":"20"}],"content_hash":"ccb9d98f31fd9493e9dbee3b570d782e0cf20720f68af10252cf459b9f27ae68","editorial":{},"first_version":"19","group":"misc","imported_at":"2026-09-30T00:35:05.79743+08:00","last_version":"20","name":"WAIT","object":"","position":16002,"present_in":["19","20"],"purpose":"","purpose_zh":"等待 WAL 达到目标 LSN","related":[],"slug":"wait","source_rev":"1b5ca64c","synopsis":"WAIT FOR LSN 'lsn'\n[ WITH ( option [, ...] ) ]\n\n其中 option 可以是：\n\nMODE 'mode'\nTIMEOUT 'timeout'\nNO_THROW\n\n而 mode 可以是：\n\nstandby_replay | standby_write | standby_flush | primary_flush","verb":"WAIT"}},"Definition":{"Collection":"sql","Key":"wait","SourceDatabase":"pgweb","Version":"20","SourceTable":"sqlcmd","SourceKey":"wait","SourceRevision":"1b5ca64c","Facts":{"anchor":"SQL-WAIT","file":"sql-wait.html","lang":"zh","name":"WAIT","purpose":"","purpose_zh":"等待 WAL 达到目标 LSN","related":[],"sections":[{"html":"\u003cp\u003e等待直到根据指定的 \u003cem class=\"parameter\"\u003e\u003ccode\u003emode\u003c/code\u003e\u003c/em\u003e 达到指定的 \u003cem class=\"parameter\"\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e，该模式决定等待 WAL 被写入、刷盘还是重放。如果没有指定 \u003cem class=\"parameter\"\u003e\u003ccode\u003etimeout\u003c/code\u003e\u003c/em\u003e，或者其值为零，则此命令会无限期等待 \u003cem class=\"parameter\"\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e。\u003c/p\u003e\u003cp\u003e超时时会发出错误，除非在 WITH 子句中指定了 \u003ccode class=\"literal\"\u003eNO_THROW\u003c/code\u003e。对于备库模式（\u003ccode class=\"literal\"\u003estandby_replay\u003c/code\u003e、\u003ccode class=\"literal\"\u003estandby_write\u003c/code\u003e、\u003ccode class=\"literal\"\u003estandby_flush\u003c/code\u003e），如果在达到 \u003cem class=\"parameter\"\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e 之前服务器被提升，也会发出错误。如果指定了 \u003cem class=\"parameter\"\u003e\u003ccode\u003eNO_THROW\u003c/code\u003e\u003c/em\u003e，命令会以状态字符串报告这些结果，而不抛出错误。\u003c/p\u003e\u003cp\u003e可能的返回值是 \u003ccode class=\"literal\"\u003esuccess\u003c/code\u003e、\u003ccode class=\"literal\"\u003etimeout\u003c/code\u003e 和 \u003ccode class=\"literal\"\u003enot in recovery\u003c/code\u003e。\u003c/p\u003e","key":"description","title":"描述"},{"html":"\u003cdiv class=\"variablelist\"\u003e\u003cdl class=\"variablelist\"\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003cem class=\"replaceable\"\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e指定要等待的目标 \u003cacronym\u003eLSN\u003c/acronym\u003e。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eWITH ( \u003cem class=\"replaceable\"\u003e\u003ccode\u003eoption\u003c/code\u003e\u003c/em\u003e [, ...] )\u003c/code\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e此子句指定等待操作的可选参数。支持以下参数：\u003c/p\u003e\u003cdiv class=\"variablelist\"\u003e\u003cdl class=\"variablelist\"\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eMODE\u003c/code\u003e '\u003cem class=\"replaceable\"\u003e\u003ccode\u003emode\u003c/code\u003e\u003c/em\u003e'\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e指定要等待的 LSN 处理类型。如果未指定，默认值为 \u003ccode class=\"literal\"\u003estandby_replay\u003c/code\u003e。有效模式如下：\u003c/p\u003e\u003cdiv class=\"itemizedlist\"\u003e\u003cul class=\"itemizedlist\"\u003e\u003cli class=\"listitem\"\u003e\u003cp\u003e\u003ccode class=\"literal\"\u003estandby_replay\u003c/code\u003e：等待该 LSN 在备库上被重放（应用到数据库）。成功完成后，\u003ccode class=\"function\"\u003epg_last_wal_replay_lsn()\u003c/code\u003e 将返回大于或等于目标 LSN 的值。此模式只能在恢复期间使用。\u003c/p\u003e\u003c/li\u003e\u003cli class=\"listitem\"\u003e\u003cp\u003e\u003ccode class=\"literal\"\u003estandby_write\u003c/code\u003e：等待包含该 LSN 的 WAL 被写入备库磁盘，但不一定已经刷盘。这比 \u003ccode class=\"literal\"\u003estandby_flush\u003c/code\u003e 更快，但持久性保证更弱，因为数据可能仍在操作系统缓冲区中。备库上已有的 WAL（来自基础备份、归档恢复或先前的流式传输）以及从主库新接收的 WAL 均可满足此条件。此模式只能在恢复期间使用。\u003c/p\u003e\u003c/li\u003e\u003cli class=\"listitem\"\u003e\u003cp\u003e\u003ccode class=\"literal\"\u003estandby_flush\u003c/code\u003e：等待包含该 LSN 的 WAL 在备库上刷盘。这提供了持久性保证，而无需等待 WAL 被应用。备库上已有的 WAL（来自基础备份、归档恢复或先前的流式传输）以及从主库新接收的 WAL 均可满足此条件。此模式只能在恢复期间使用。\u003c/p\u003e\u003c/li\u003e\u003cli class=\"listitem\"\u003e\u003cp\u003e\u003ccode class=\"literal\"\u003eprimary_flush\u003c/code\u003e：等待包含该 LSN 的 WAL 在主库上刷盘。成功完成后，\u003ccode class=\"function\"\u003epg_current_wal_flush_lsn()\u003c/code\u003e 将返回大于或等于目标 LSN 的值。此模式只能在主库上使用（不能在恢复期间使用）。\u003c/p\u003e\u003c/li\u003e\u003c/ul\u003e\u003c/div\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eTIMEOUT\u003c/code\u003e '\u003cem class=\"replaceable\"\u003e\u003ccode\u003etimeout\u003c/code\u003e\u003c/em\u003e'\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e在指定且 \u003cem class=\"parameter\"\u003e\u003ccode\u003etimeout\u003c/code\u003e\u003c/em\u003e 大于零时，命令会一直等待到 \u003cem class=\"parameter\"\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e 达到，或者直到指定的 \u003cem class=\"parameter\"\u003e\u003ccode\u003etimeout\u003c/code\u003e\u003c/em\u003e 到期为止。值为零（默认值）表示命令会无限期等待。\u003c/p\u003e\u003cp\u003e\u003cem class=\"parameter\"\u003e\u003ccode\u003etimeout\u003c/code\u003e\u003c/em\u003e 是以毫秒为单位的时长。也可以将其指定为一个字符串，其中包含数值及其后的时间单位（见\u003ca href=\"/docs/devel/config-setting.html#CONFIG-SETTING-NAMES-VALUES\" title=\"19.1.1. 参数名称和值\"\u003e第 19.1.1 节\u003c/a\u003e）。最大值为 \u003ccode class=\"literal\"\u003e2147483647 ms\u003c/code\u003e。\u003c/p\u003e\u003cp\u003e小数值会舍入到最接近的毫秒数。因此，注意半毫秒或更短的 \u003cem class=\"parameter\"\u003e\u003ccode\u003etimeout\u003c/code\u003e\u003c/em\u003e 会向下舍入为零，这意味着无限期等待。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eNO_THROW\u003c/code\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e指定在超时或在主库上运行时不抛出错误。在这种情况下，可以从返回值中获取结果状态。\u003c/p\u003e\u003cp\u003e当 \u003ccode class=\"literal\"\u003etimeout\u003c/code\u003e 或 \u003ccode class=\"literal\"\u003enot in recovery\u003c/code\u003e 是应用程序预期并打算处理的结果时，可使用此选项，例如重试等待、报告复制延迟，或为后续操作选择另一台服务器。命令随后会将结果作为状态返回；应用程序必须检查该状态，才能认定已达到目标 \u003cacronym\u003eLSN\u003c/acronym\u003e。如果应用程序必须在达到目标 \u003cacronym\u003eLSN\u003c/acronym\u003e 后才能继续，则应省略此选项，这样等待未成功时就会通过错误停止正常执行。\u003c/p\u003e\u003cp\u003e返回状态也会使显式事务保持可用；如果不使用此选项，相应错误会要求先回滚事务，或回滚到保存点，才能发出后续命令。\u003c/p\u003e\u003cp\u003e此选项只改变 \u003ccode class=\"literal\"\u003etimeout\u003c/code\u003e 和 \u003ccode class=\"literal\"\u003enot in recovery\u003c/code\u003e 的报告方式。其他错误仍会抛出，包括无效输入（例如格式错误的 \u003cacronym\u003eLSN\u003c/acronym\u003e 或无法识别的选项值），以及等待开始前检查的所有条件（例如在恢复期间请求 \u003ccode class=\"literal\"\u003eprimary_flush\u003c/code\u003e，或在持有锁的同时等待备库 \u003cacronym\u003eLSN\u003c/acronym\u003e）。此选项也不限制等待时长；要限制时长，应指定 \u003ccode class=\"literal\"\u003eTIMEOUT\u003c/code\u003e。\u003c/p\u003e\u003c/dd\u003e\u003c/dl\u003e\u003c/div\u003e\u003c/dd\u003e\u003c/dl\u003e\u003c/div\u003e","key":"parameters","title":"参数"},{"html":"\u003cdiv class=\"variablelist\"\u003e\u003cdl class=\"variablelist\"\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003esuccess\u003c/code\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e此返回值表示已成功达到目标 \u003cem class=\"parameter\"\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003etimeout\u003c/code\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e此返回值表示在达到目标 \u003cem class=\"parameter\"\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e 之前发生了超时。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003enot in recovery\u003c/code\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e此返回值表示数据库服务器不处于恢复状态。这可能意味着数据库服务器在接收到命令时并不处于恢复状态（即在主库上执行），也可能意味着它在达到目标 \u003cem class=\"parameter\"\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e 之前被提升。在提升的情况下，此状态表示发生了时间线变更，应用程序应重新评估目标 LSN 是否仍然适用。\u003c/p\u003e\u003c/dd\u003e\u003c/dl\u003e\u003c/div\u003e","key":"outputs","title":"输出"},{"html":"\u003cp\u003e\u003ccode class=\"command\"\u003eWAIT\u003c/code\u003e 必须作为顶层命令执行，不能从函数、过程或 \u003ccode class=\"command\"\u003eDO\u003c/code\u003e 块中执行。当前事务持有快照时也不能执行。\u003ccode class=\"command\"\u003eWAIT\u003c/code\u003e 本身不获取快照，因此可以在 \u003ccode class=\"literal\"\u003eREPEATABLE READ\u003c/code\u003e 或 \u003ccode class=\"literal\"\u003eSERIALIZABLE\u003c/code\u003e 事务中第一条获取快照的语句之前运行，但不能在该语句之后运行；无论处于何种隔离级别，如果游标或导出的快照使事务持有快照，也不能运行。此处持有的快照可能延迟重放，而 \u003ccode class=\"literal\"\u003estandby_replay\u003c/code\u003e 会等待重放，其他备库模式最终也可能需要等待重放。这也是 \u003ccode class=\"command\"\u003eWAIT\u003c/code\u003e 被实现为命令而不是函数或过程的原因，因为函数和过程执行时会持有快照。\u003c/p\u003e\u003cp\u003e恢复进行期间，如果会话已经持有锁，且尚未达到目标 \u003cem class=\"parameter\"\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e，则会拒绝 \u003ccode class=\"literal\"\u003estandby_replay\u003c/code\u003e（默认值）、\u003ccode class=\"literal\"\u003estandby_write\u003c/code\u003e 或 \u003ccode class=\"literal\"\u003estandby_flush\u003c/code\u003e 模式的等待。这样的锁可能使启动进程直接或通过另一个会话等待当前会话，而当前会话又在等待启动进程推进恢复。这个循环在当前会话一侧不涉及锁等待，因而死锁检测无法发现，也没有机制可以打破它。如果已经达到目标，等待会立即返回，因此始终允许。\u003c/p\u003e\u003cp\u003e应在事务块之外发出 \u003ccode class=\"command\"\u003eWAIT FOR\u003c/code\u003e，或将其作为事务块的第一条语句，在运行任何会获取锁的操作之前执行。这也是下文示例所示读己之写模式的自然顺序：先等待目标 \u003cem class=\"parameter\"\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e，再运行必须看到相应更改的查询。注意，在 \u003ccode class=\"literal\"\u003eREAD COMMITTED\u003c/code\u003e 下，即使先前语句的快照已经释放，它获取的锁仍会保留。因此，即使隔离级别允许等待，在这类语句之后执行等待仍会被拒绝。\u003c/p\u003e\u003cp\u003e此限制也适用于 \u003ccode class=\"literal\"\u003estandby_write\u003c/code\u003e 和 \u003ccode class=\"literal\"\u003estandby_flush\u003c/code\u003e，尽管流复制可以在不依赖启动进程的情况下推进这两个位置。这两个位置都不会落后于重放位置，因此当 walreceiver 未处于活动状态时，重放可能是推进它们的唯一途径。如果所持有的锁阻塞了重放，会话就在等待重放推进，而重放又在等待会话释放锁。在流复制下，只有 WAL 持续到达时，这些位置才会独立推进。如果接收在达到目标之前停止，被阻塞的启动进程就无法重新启动 walreceiver。它也无法重放推进重启点和回收 WAL 所需的较新检查点记录，因此 \u003ccode class=\"filename\"\u003epg_wal\u003c/code\u003e 可能被填满，导致接收在达到目标之前停止。所以，即使等待开始时流式传输处于活动状态，此限制也仍然适用。\u003c/p\u003e\u003cp\u003e\u003ccode class=\"command\"\u003eWAIT\u003c/code\u003e 会根据指定的 \u003cem class=\"parameter\"\u003e\u003ccode\u003emode\u003c/code\u003e\u003c/em\u003e 一直等待直到达到指定的 \u003cem class=\"parameter\"\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e。\u003ccode class=\"literal\"\u003estandby_replay\u003c/code\u003e 模式会等待该 LSN 被重放（应用到数据库），这有助于在使用异步副本读取和主库写入时实现读己之写一致性，前提是目标 \u003cacronym\u003eLSN\u003c/acronym\u003e 位于主库上相关写事务的 \u003ccode class=\"literal\"\u003eCOMMIT\u003c/code\u003e 记录末尾或其后。\u003ccode class=\"literal\"\u003estandby_flush\u003c/code\u003e 模式会等待 WAL 在副本上刷入持久存储，或者等待备库上已有的 WAL 已被重放。\u003ccode class=\"literal\"\u003estandby_write\u003c/code\u003e 模式会等待 WAL 被写入操作系统，或者已经重放；对于新接收的 WAL，这比刷盘更快，但持久性保证更弱。\u003ccode class=\"literal\"\u003eprimary_flush\u003c/code\u003e 模式会等待 WAL 在主库上刷盘。在所有情况下，最后一次修改的 \u003cacronym\u003eLSN\u003c/acronym\u003e 应当存储在客户端应用程序一侧或连接池一侧。\u003c/p\u003e\u003cp\u003e备库模式（\u003ccode class=\"literal\"\u003estandby_replay\u003c/code\u003e、\u003ccode class=\"literal\"\u003estandby_write\u003c/code\u003e、\u003ccode class=\"literal\"\u003estandby_flush\u003c/code\u003e）只能在恢复期间使用，而 \u003ccode class=\"literal\"\u003eprimary_flush\u003c/code\u003e 只能在主库上使用。对当前服务器状态使用错误的模式会导致错误。如果在使用备库模式等待期间备库被提升，命令将返回 \u003ccode class=\"literal\"\u003enot in recovery\u003c/code\u003e（或者在未指定 \u003ccode class=\"literal\"\u003eNO_THROW\u003c/code\u003e 时抛出错误）。提升会创建新的时间线，而正在等待的 LSN 可能指向旧时间线中的 WAL。\u003c/p\u003e\u003cp\u003e\u003ccode class=\"command\"\u003eWAIT\u003c/code\u003e只比较\u003cacronym\u003eLSN\u003c/acronym\u003e数值；它并不知道 WAL 记录属于哪条时间线。当备库跨上游时间线切换继续恢复时，这一点很重要—例如，级联备库的上游被提升。在这种情况下，只要所选等待模式使用的位置达到或超过该\u003cacronym\u003eLSN\u003c/acronym\u003e数值，\u003ccode class=\"command\"\u003eWAIT\u003c/code\u003e就会返回\u003ccode class=\"literal\"\u003esuccess\u003c/code\u003e，而不考虑该 \u003cacronym\u003eLSN\u003c/acronym\u003e属于哪条时间线。需要确认目标指向预期时间线的应用程序必须自行验证时间线。\u003c/p\u003e\u003cp\u003e在备库服务器上，\u003ccode class=\"command\"\u003eWAIT\u003c/code\u003e 会话可能会被恢复冲突中断。某些恢复冲突是不可避免的：例如，重放一个表空间删除操作时，无论后端正在执行什么，都会通过终止所有后端来解决冲突。在备库上使用 \u003ccode class=\"command\"\u003eWAIT\u003c/code\u003e 的应用程序应准备好处理此类中断，例如通过重试命令或回退到其他机制。\u003c/p\u003e","key":"notes","title":"注解"},{"html":"\u003cp\u003e可以使用 \u003ccode class=\"command\"\u003eWAIT\u003c/code\u003e 命令等待 \u003ccode class=\"type\"\u003epg_lsn\u003c/code\u003e 值。例如，应用程序可以更新 \u003ccode class=\"literal\"\u003emovie\u003c/code\u003e 表，并获取位于相关写事务的 \u003ccode class=\"literal\"\u003eCOMMIT\u003c/code\u003e 记录末尾或其后的 \u003cacronym\u003elsn\u003c/acronym\u003e。在这里展示的默认自动提交模式下，\u003ccode class=\"command\"\u003eUPDATE\u003c/code\u003e 会在后续的 \u003ccode class=\"command\"\u003eSELECT\u003c/code\u003e 之前提交。由于 \u003ccode class=\"varname\"\u003esynchronous_commit\u003c/code\u003e 可能设置为 \u003ccode class=\"literal\"\u003eoff\u003c/code\u003e，此示例在主库上使用 \u003ccode class=\"function\"\u003epg_current_wal_insert_lsn\u003c/code\u003e 获取 \u003cacronym\u003elsn\u003c/acronym\u003e。\u003c/p\u003e\u003cpre class=\"programlisting\"\u003epostgres=# UPDATE movie SET genre = 'Dramatic' WHERE genre = 'Drama';\nUPDATE 100\npostgres=# SELECT pg_current_wal_insert_lsn();\n pg_current_wal_insert_lsn\n---------------------------\n 0/0306EE20\n(1 row)\n\u003c/pre\u003e\u003cp\u003e然后，应用程序可以使用提交之后从主库获得的 \u003cem class=\"parameter\"\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e 运行 \u003ccode class=\"command\"\u003eWAIT\u003c/code\u003e。之后，主库上所做的更改应当保证在副本上可见。\u003c/p\u003e\u003cpre class=\"programlisting\"\u003epostgres=# WAIT FOR LSN '0/0306EE20';\n status\n---------\n success\n(1 row)\npostgres=# SELECT * FROM movie WHERE genre = 'Drama';\n genre\n-------\n(0 rows)\n\u003c/pre\u003e\u003cp\u003e等待刷盘（数据在副本上持久化）：\u003c/p\u003e\u003cpre class=\"programlisting\"\u003epostgres=# WAIT FOR LSN '0/0306EE20' WITH (MODE 'standby_flush');\n status\n---------\n success\n(1 row)\n\u003c/pre\u003e\u003cp\u003e带超时的等待写入：\u003c/p\u003e\u003cpre class=\"programlisting\"\u003epostgres=# WAIT FOR LSN '0/0306EE20' WITH (MODE 'standby_write', TIMEOUT '100ms', NO_THROW);\n status\n---------\n success\n(1 row)\n\u003c/pre\u003e\u003cp\u003e等待主库刷盘：\u003c/p\u003e\u003cpre class=\"programlisting\"\u003epostgres=# WAIT FOR LSN '0/0306EE20' WITH (MODE 'primary_flush');\n status\n---------\n success\n(1 row)\n\u003c/pre\u003e\u003cp\u003e如果在超时之前未达到目标 LSN，则会抛出错误：\u003c/p\u003e\u003cpre class=\"programlisting\"\u003epostgres=# WAIT FOR LSN '0/0306EE20' WITH (TIMEOUT '0.1s');\nERROR:  timed out while waiting for target LSN 0/0306EE20 to be replayed; current standby_replay LSN 0/0306EA60\n\u003c/pre\u003e\u003cp\u003e同一个示例使用带 \u003cem class=\"parameter\"\u003e\u003ccode\u003eNO_THROW\u003c/code\u003e\u003c/em\u003e 选项的 \u003ccode class=\"command\"\u003eWAIT\u003c/code\u003e：\u003c/p\u003e\u003cpre class=\"programlisting\"\u003epostgres=# WAIT FOR LSN '0/0306EE20' WITH (TIMEOUT '100ms', NO_THROW);\n status\n---------\n timeout\n(1 row)\n\u003c/pre\u003e","key":"examples","title":"示例"}],"sections_same_as":"","slug":"devel","synopsis_html":"WAIT FOR LSN '\u003cem class=\"replaceable\"\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e'\n    [ WITH ( \u003cem class=\"replaceable\"\u003e\u003ccode\u003eoption\u003c/code\u003e\u003c/em\u003e [, ...] ) ]\n\n\u003cspan class=\"phrase\"\u003e其中 \u003cem class=\"replaceable\"\u003e\u003ccode\u003eoption\u003c/code\u003e\u003c/em\u003e 可以是：\u003c/span\u003e\n\n    MODE '\u003cem class=\"replaceable\"\u003e\u003ccode\u003emode\u003c/code\u003e\u003c/em\u003e'\n    TIMEOUT '\u003cem class=\"replaceable\"\u003e\u003ccode\u003etimeout\u003c/code\u003e\u003c/em\u003e'\n    NO_THROW\n\n\u003cspan class=\"phrase\"\u003e而 \u003cem class=\"replaceable\"\u003e\u003ccode\u003emode\u003c/code\u003e\u003c/em\u003e 可以是：\u003c/span\u003e\n\n    standby_replay | standby_write | standby_flush | primary_flush","synopsis_text":"WAIT FOR LSN 'lsn'\n[ WITH ( option [, ...] ) ]\n\n其中 option 可以是：\n\nMODE 'mode'\nTIMEOUT 'timeout'\nNO_THROW\n\n而 mode 可以是：\n\nstandby_replay | standby_write | standby_flush | primary_flush"},"ManualEvidence":{},"MeasuredEvidence":{}},"Text":{"Collection":"sql","Key":"wait","SourceDatabase":"pgweb","Version":"20","Locale":"zh-Hans","Title":"WAIT","Summary":"等待 WAL 达到目标 LSN","BodyHTML":"\u003cpre\u003eWAIT FOR LSN \u0026#39;lsn\u0026#39;\n[ WITH ( option [, ...] ) ]\n\n其中 option 可以是：\n\nMODE \u0026#39;mode\u0026#39;\nTIMEOUT \u0026#39;timeout\u0026#39;\nNO_THROW\n\n而 mode 可以是：\n\nstandby_replay | standby_write | standby_flush | primary_flush\u003c/pre\u003e\u003csection\u003e\u003ch2\u003e描述\u003c/h2\u003e\u003cp\u003e等待直到根据指定的 \u003cem\u003e\u003ccode\u003emode\u003c/code\u003e\u003c/em\u003e 达到指定的 \u003cem\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e，该模式决定等待 WAL 被写入、刷盘还是重放。如果没有指定 \u003cem\u003e\u003ccode\u003etimeout\u003c/code\u003e\u003c/em\u003e，或者其值为零，则此命令会无限期等待 \u003cem\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e。\u003c/p\u003e\u003cp\u003e超时时会发出错误，除非在 WITH 子句中指定了 \u003ccode\u003eNO_THROW\u003c/code\u003e。对于备库模式（\u003ccode\u003estandby_replay\u003c/code\u003e、\u003ccode\u003estandby_write\u003c/code\u003e、\u003ccode\u003estandby_flush\u003c/code\u003e），如果在达到 \u003cem\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e 之前服务器被提升，也会发出错误。如果指定了 \u003cem\u003e\u003ccode\u003eNO_THROW\u003c/code\u003e\u003c/em\u003e，命令会以状态字符串报告这些结果，而不抛出错误。\u003c/p\u003e\u003cp\u003e可能的返回值是 \u003ccode\u003esuccess\u003c/code\u003e、\u003ccode\u003etimeout\u003c/code\u003e 和 \u003ccode\u003enot in recovery\u003c/code\u003e。\u003c/p\u003e\u003c/section\u003e\u003csection\u003e\u003ch2\u003e参数\u003c/h2\u003e\u003cdiv\u003e\u003cdl\u003e\u003cdt\u003e\u003cspan\u003e\u003cem\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e指定要等待的目标 \u003cacronym\u003eLSN\u003c/acronym\u003e。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan\u003e\u003ccode\u003eWITH ( \u003cem\u003e\u003ccode\u003eoption\u003c/code\u003e\u003c/em\u003e [, ...] )\u003c/code\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e此子句指定等待操作的可选参数。支持以下参数：\u003c/p\u003e\u003cdiv\u003e\u003cdl\u003e\u003cdt\u003e\u003cspan\u003e\u003ccode\u003eMODE\u003c/code\u003e \u0026#39;\u003cem\u003e\u003ccode\u003emode\u003c/code\u003e\u003c/em\u003e\u0026#39;\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e指定要等待的 LSN 处理类型。如果未指定，默认值为 \u003ccode\u003estandby_replay\u003c/code\u003e。有效模式如下：\u003c/p\u003e\u003cdiv\u003e\u003cul\u003e\u003cli\u003e\u003cp\u003e\u003ccode\u003estandby_replay\u003c/code\u003e：等待该 LSN 在备库上被重放（应用到数据库）。成功完成后，\u003ccode\u003epg_last_wal_replay_lsn()\u003c/code\u003e 将返回大于或等于目标 LSN 的值。此模式只能在恢复期间使用。\u003c/p\u003e\u003c/li\u003e\u003cli\u003e\u003cp\u003e\u003ccode\u003estandby_write\u003c/code\u003e：等待包含该 LSN 的 WAL 被写入备库磁盘，但不一定已经刷盘。这比 \u003ccode\u003estandby_flush\u003c/code\u003e 更快，但持久性保证更弱，因为数据可能仍在操作系统缓冲区中。备库上已有的 WAL（来自基础备份、归档恢复或先前的流式传输）以及从主库新接收的 WAL 均可满足此条件。此模式只能在恢复期间使用。\u003c/p\u003e\u003c/li\u003e\u003cli\u003e\u003cp\u003e\u003ccode\u003estandby_flush\u003c/code\u003e：等待包含该 LSN 的 WAL 在备库上刷盘。这提供了持久性保证，而无需等待 WAL 被应用。备库上已有的 WAL（来自基础备份、归档恢复或先前的流式传输）以及从主库新接收的 WAL 均可满足此条件。此模式只能在恢复期间使用。\u003c/p\u003e\u003c/li\u003e\u003cli\u003e\u003cp\u003e\u003ccode\u003eprimary_flush\u003c/code\u003e：等待包含该 LSN 的 WAL 在主库上刷盘。成功完成后，\u003ccode\u003epg_current_wal_flush_lsn()\u003c/code\u003e 将返回大于或等于目标 LSN 的值。此模式只能在主库上使用（不能在恢复期间使用）。\u003c/p\u003e\u003c/li\u003e\u003c/ul\u003e\u003c/div\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan\u003e\u003ccode\u003eTIMEOUT\u003c/code\u003e \u0026#39;\u003cem\u003e\u003ccode\u003etimeout\u003c/code\u003e\u003c/em\u003e\u0026#39;\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e在指定且 \u003cem\u003e\u003ccode\u003etimeout\u003c/code\u003e\u003c/em\u003e 大于零时，命令会一直等待到 \u003cem\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e 达到，或者直到指定的 \u003cem\u003e\u003ccode\u003etimeout\u003c/code\u003e\u003c/em\u003e 到期为止。值为零（默认值）表示命令会无限期等待。\u003c/p\u003e\u003cp\u003e\u003cem\u003e\u003ccode\u003etimeout\u003c/code\u003e\u003c/em\u003e 是以毫秒为单位的时长。也可以将其指定为一个字符串，其中包含数值及其后的时间单位（见\u003ca href=\"/docs/devel/config-setting.html#CONFIG-SETTING-NAMES-VALUES\" rel=\"nofollow\"\u003e第 19.1.1 节\u003c/a\u003e）。最大值为 \u003ccode\u003e2147483647 ms\u003c/code\u003e。\u003c/p\u003e\u003cp\u003e小数值会舍入到最接近的毫秒数。因此，注意半毫秒或更短的 \u003cem\u003e\u003ccode\u003etimeout\u003c/code\u003e\u003c/em\u003e 会向下舍入为零，这意味着无限期等待。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan\u003e\u003ccode\u003eNO_THROW\u003c/code\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e指定在超时或在主库上运行时不抛出错误。在这种情况下，可以从返回值中获取结果状态。\u003c/p\u003e\u003cp\u003e当 \u003ccode\u003etimeout\u003c/code\u003e 或 \u003ccode\u003enot in recovery\u003c/code\u003e 是应用程序预期并打算处理的结果时，可使用此选项，例如重试等待、报告复制延迟，或为后续操作选择另一台服务器。命令随后会将结果作为状态返回；应用程序必须检查该状态，才能认定已达到目标 \u003cacronym\u003eLSN\u003c/acronym\u003e。如果应用程序必须在达到目标 \u003cacronym\u003eLSN\u003c/acronym\u003e 后才能继续，则应省略此选项，这样等待未成功时就会通过错误停止正常执行。\u003c/p\u003e\u003cp\u003e返回状态也会使显式事务保持可用；如果不使用此选项，相应错误会要求先回滚事务，或回滚到保存点，才能发出后续命令。\u003c/p\u003e\u003cp\u003e此选项只改变 \u003ccode\u003etimeout\u003c/code\u003e 和 \u003ccode\u003enot in recovery\u003c/code\u003e 的报告方式。其他错误仍会抛出，包括无效输入（例如格式错误的 \u003cacronym\u003eLSN\u003c/acronym\u003e 或无法识别的选项值），以及等待开始前检查的所有条件（例如在恢复期间请求 \u003ccode\u003eprimary_flush\u003c/code\u003e，或在持有锁的同时等待备库 \u003cacronym\u003eLSN\u003c/acronym\u003e）。此选项也不限制等待时长；要限制时长，应指定 \u003ccode\u003eTIMEOUT\u003c/code\u003e。\u003c/p\u003e\u003c/dd\u003e\u003c/dl\u003e\u003c/div\u003e\u003c/dd\u003e\u003c/dl\u003e\u003c/div\u003e\u003c/section\u003e\u003csection\u003e\u003ch2\u003e输出\u003c/h2\u003e\u003cdiv\u003e\u003cdl\u003e\u003cdt\u003e\u003cspan\u003e\u003ccode\u003esuccess\u003c/code\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e此返回值表示已成功达到目标 \u003cem\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan\u003e\u003ccode\u003etimeout\u003c/code\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e此返回值表示在达到目标 \u003cem\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e 之前发生了超时。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan\u003e\u003ccode\u003enot in recovery\u003c/code\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e此返回值表示数据库服务器不处于恢复状态。这可能意味着数据库服务器在接收到命令时并不处于恢复状态（即在主库上执行），也可能意味着它在达到目标 \u003cem\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e 之前被提升。在提升的情况下，此状态表示发生了时间线变更，应用程序应重新评估目标 LSN 是否仍然适用。\u003c/p\u003e\u003c/dd\u003e\u003c/dl\u003e\u003c/div\u003e\u003c/section\u003e\u003csection\u003e\u003ch2\u003e注解\u003c/h2\u003e\u003cp\u003e\u003ccode\u003eWAIT\u003c/code\u003e 必须作为顶层命令执行，不能从函数、过程或 \u003ccode\u003eDO\u003c/code\u003e 块中执行。当前事务持有快照时也不能执行。\u003ccode\u003eWAIT\u003c/code\u003e 本身不获取快照，因此可以在 \u003ccode\u003eREPEATABLE READ\u003c/code\u003e 或 \u003ccode\u003eSERIALIZABLE\u003c/code\u003e 事务中第一条获取快照的语句之前运行，但不能在该语句之后运行；无论处于何种隔离级别，如果游标或导出的快照使事务持有快照，也不能运行。此处持有的快照可能延迟重放，而 \u003ccode\u003estandby_replay\u003c/code\u003e 会等待重放，其他备库模式最终也可能需要等待重放。这也是 \u003ccode\u003eWAIT\u003c/code\u003e 被实现为命令而不是函数或过程的原因，因为函数和过程执行时会持有快照。\u003c/p\u003e\u003cp\u003e恢复进行期间，如果会话已经持有锁，且尚未达到目标 \u003cem\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e，则会拒绝 \u003ccode\u003estandby_replay\u003c/code\u003e（默认值）、\u003ccode\u003estandby_write\u003c/code\u003e 或 \u003ccode\u003estandby_flush\u003c/code\u003e 模式的等待。这样的锁可能使启动进程直接或通过另一个会话等待当前会话，而当前会话又在等待启动进程推进恢复。这个循环在当前会话一侧不涉及锁等待，因而死锁检测无法发现，也没有机制可以打破它。如果已经达到目标，等待会立即返回，因此始终允许。\u003c/p\u003e\u003cp\u003e应在事务块之外发出 \u003ccode\u003eWAIT FOR\u003c/code\u003e，或将其作为事务块的第一条语句，在运行任何会获取锁的操作之前执行。这也是下文示例所示读己之写模式的自然顺序：先等待目标 \u003cem\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e，再运行必须看到相应更改的查询。注意，在 \u003ccode\u003eREAD COMMITTED\u003c/code\u003e 下，即使先前语句的快照已经释放，它获取的锁仍会保留。因此，即使隔离级别允许等待，在这类语句之后执行等待仍会被拒绝。\u003c/p\u003e\u003cp\u003e此限制也适用于 \u003ccode\u003estandby_write\u003c/code\u003e 和 \u003ccode\u003estandby_flush\u003c/code\u003e，尽管流复制可以在不依赖启动进程的情况下推进这两个位置。这两个位置都不会落后于重放位置，因此当 walreceiver 未处于活动状态时，重放可能是推进它们的唯一途径。如果所持有的锁阻塞了重放，会话就在等待重放推进，而重放又在等待会话释放锁。在流复制下，只有 WAL 持续到达时，这些位置才会独立推进。如果接收在达到目标之前停止，被阻塞的启动进程就无法重新启动 walreceiver。它也无法重放推进重启点和回收 WAL 所需的较新检查点记录，因此 \u003ccode\u003epg_wal\u003c/code\u003e 可能被填满，导致接收在达到目标之前停止。所以，即使等待开始时流式传输处于活动状态，此限制也仍然适用。\u003c/p\u003e\u003cp\u003e\u003ccode\u003eWAIT\u003c/code\u003e 会根据指定的 \u003cem\u003e\u003ccode\u003emode\u003c/code\u003e\u003c/em\u003e 一直等待直到达到指定的 \u003cem\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e。\u003ccode\u003estandby_replay\u003c/code\u003e 模式会等待该 LSN 被重放（应用到数据库），这有助于在使用异步副本读取和主库写入时实现读己之写一致性，前提是目标 \u003cacronym\u003eLSN\u003c/acronym\u003e 位于主库上相关写事务的 \u003ccode\u003eCOMMIT\u003c/code\u003e 记录末尾或其后。\u003ccode\u003estandby_flush\u003c/code\u003e 模式会等待 WAL 在副本上刷入持久存储，或者等待备库上已有的 WAL 已被重放。\u003ccode\u003estandby_write\u003c/code\u003e 模式会等待 WAL 被写入操作系统，或者已经重放；对于新接收的 WAL，这比刷盘更快，但持久性保证更弱。\u003ccode\u003eprimary_flush\u003c/code\u003e 模式会等待 WAL 在主库上刷盘。在所有情况下，最后一次修改的 \u003cacronym\u003eLSN\u003c/acronym\u003e 应当存储在客户端应用程序一侧或连接池一侧。\u003c/p\u003e\u003cp\u003e备库模式（\u003ccode\u003estandby_replay\u003c/code\u003e、\u003ccode\u003estandby_write\u003c/code\u003e、\u003ccode\u003estandby_flush\u003c/code\u003e）只能在恢复期间使用，而 \u003ccode\u003eprimary_flush\u003c/code\u003e 只能在主库上使用。对当前服务器状态使用错误的模式会导致错误。如果在使用备库模式等待期间备库被提升，命令将返回 \u003ccode\u003enot in recovery\u003c/code\u003e（或者在未指定 \u003ccode\u003eNO_THROW\u003c/code\u003e 时抛出错误）。提升会创建新的时间线，而正在等待的 LSN 可能指向旧时间线中的 WAL。\u003c/p\u003e\u003cp\u003e\u003ccode\u003eWAIT\u003c/code\u003e只比较\u003cacronym\u003eLSN\u003c/acronym\u003e数值；它并不知道 WAL 记录属于哪条时间线。当备库跨上游时间线切换继续恢复时，这一点很重要—例如，级联备库的上游被提升。在这种情况下，只要所选等待模式使用的位置达到或超过该\u003cacronym\u003eLSN\u003c/acronym\u003e数值，\u003ccode\u003eWAIT\u003c/code\u003e就会返回\u003ccode\u003esuccess\u003c/code\u003e，而不考虑该 \u003cacronym\u003eLSN\u003c/acronym\u003e属于哪条时间线。需要确认目标指向预期时间线的应用程序必须自行验证时间线。\u003c/p\u003e\u003cp\u003e在备库服务器上，\u003ccode\u003eWAIT\u003c/code\u003e 会话可能会被恢复冲突中断。某些恢复冲突是不可避免的：例如，重放一个表空间删除操作时，无论后端正在执行什么，都会通过终止所有后端来解决冲突。在备库上使用 \u003ccode\u003eWAIT\u003c/code\u003e 的应用程序应准备好处理此类中断，例如通过重试命令或回退到其他机制。\u003c/p\u003e\u003c/section\u003e\u003csection\u003e\u003ch2\u003e示例\u003c/h2\u003e\u003cp\u003e可以使用 \u003ccode\u003eWAIT\u003c/code\u003e 命令等待 \u003ccode\u003epg_lsn\u003c/code\u003e 值。例如，应用程序可以更新 \u003ccode\u003emovie\u003c/code\u003e 表，并获取位于相关写事务的 \u003ccode\u003eCOMMIT\u003c/code\u003e 记录末尾或其后的 \u003cacronym\u003elsn\u003c/acronym\u003e。在这里展示的默认自动提交模式下，\u003ccode\u003eUPDATE\u003c/code\u003e 会在后续的 \u003ccode\u003eSELECT\u003c/code\u003e 之前提交。由于 \u003ccode\u003esynchronous_commit\u003c/code\u003e 可能设置为 \u003ccode\u003eoff\u003c/code\u003e，此示例在主库上使用 \u003ccode\u003epg_current_wal_insert_lsn\u003c/code\u003e 获取 \u003cacronym\u003elsn\u003c/acronym\u003e。\u003c/p\u003e\u003cpre\u003epostgres=# UPDATE movie SET genre = \u0026#39;Dramatic\u0026#39; WHERE genre = \u0026#39;Drama\u0026#39;;\nUPDATE 100\npostgres=# SELECT pg_current_wal_insert_lsn();\n pg_current_wal_insert_lsn\n---------------------------\n 0/0306EE20\n(1 row)\n\u003c/pre\u003e\u003cp\u003e然后，应用程序可以使用提交之后从主库获得的 \u003cem\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e 运行 \u003ccode\u003eWAIT\u003c/code\u003e。之后，主库上所做的更改应当保证在副本上可见。\u003c/p\u003e\u003cpre\u003epostgres=# WAIT FOR LSN \u0026#39;0/0306EE20\u0026#39;;\n status\n---------\n success\n(1 row)\npostgres=# SELECT * FROM movie WHERE genre = \u0026#39;Drama\u0026#39;;\n genre\n-------\n(0 rows)\n\u003c/pre\u003e\u003cp\u003e等待刷盘（数据在副本上持久化）：\u003c/p\u003e\u003cpre\u003epostgres=# WAIT FOR LSN \u0026#39;0/0306EE20\u0026#39; WITH (MODE \u0026#39;standby_flush\u0026#39;);\n status\n---------\n success\n(1 row)\n\u003c/pre\u003e\u003cp\u003e带超时的等待写入：\u003c/p\u003e\u003cpre\u003epostgres=# WAIT FOR LSN \u0026#39;0/0306EE20\u0026#39; WITH (MODE \u0026#39;standby_write\u0026#39;, TIMEOUT \u0026#39;100ms\u0026#39;, NO_THROW);\n status\n---------\n success\n(1 row)\n\u003c/pre\u003e\u003cp\u003e等待主库刷盘：\u003c/p\u003e\u003cpre\u003epostgres=# WAIT FOR LSN \u0026#39;0/0306EE20\u0026#39; WITH (MODE \u0026#39;primary_flush\u0026#39;);\n status\n---------\n success\n(1 row)\n\u003c/pre\u003e\u003cp\u003e如果在超时之前未达到目标 LSN，则会抛出错误：\u003c/p\u003e\u003cpre\u003epostgres=# WAIT FOR LSN \u0026#39;0/0306EE20\u0026#39; WITH (TIMEOUT \u0026#39;0.1s\u0026#39;);\nERROR:  timed out while waiting for target LSN 0/0306EE20 to be replayed; current standby_replay LSN 0/0306EA60\n\u003c/pre\u003e\u003cp\u003e同一个示例使用带 \u003cem\u003e\u003ccode\u003eNO_THROW\u003c/code\u003e\u003c/em\u003e 选项的 \u003ccode\u003eWAIT\u003c/code\u003e：\u003c/p\u003e\u003cpre\u003epostgres=# WAIT FOR LSN \u0026#39;0/0306EE20\u0026#39; WITH (TIMEOUT \u0026#39;100ms\u0026#39;, NO_THROW);\n status\n---------\n timeout\n(1 row)\n\u003c/pre\u003e\u003c/section\u003e","SourceRevision":"1b5ca64c","ContentHash":"266182a3e15af76d5c08756802702fb11be9b48e74e41a9f9b5c91babe3fafd3","Payload":{"purpose_zh":"等待 WAL 达到目标 LSN","sections":[{"html":"\u003cp\u003e等待直到根据指定的 \u003cem class=\"parameter\"\u003e\u003ccode\u003emode\u003c/code\u003e\u003c/em\u003e 达到指定的 \u003cem class=\"parameter\"\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e，该模式决定等待 WAL 被写入、刷盘还是重放。如果没有指定 \u003cem class=\"parameter\"\u003e\u003ccode\u003etimeout\u003c/code\u003e\u003c/em\u003e，或者其值为零，则此命令会无限期等待 \u003cem class=\"parameter\"\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e。\u003c/p\u003e\u003cp\u003e超时时会发出错误，除非在 WITH 子句中指定了 \u003ccode class=\"literal\"\u003eNO_THROW\u003c/code\u003e。对于备库模式（\u003ccode class=\"literal\"\u003estandby_replay\u003c/code\u003e、\u003ccode class=\"literal\"\u003estandby_write\u003c/code\u003e、\u003ccode class=\"literal\"\u003estandby_flush\u003c/code\u003e），如果在达到 \u003cem class=\"parameter\"\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e 之前服务器被提升，也会发出错误。如果指定了 \u003cem class=\"parameter\"\u003e\u003ccode\u003eNO_THROW\u003c/code\u003e\u003c/em\u003e，命令会以状态字符串报告这些结果，而不抛出错误。\u003c/p\u003e\u003cp\u003e可能的返回值是 \u003ccode class=\"literal\"\u003esuccess\u003c/code\u003e、\u003ccode class=\"literal\"\u003etimeout\u003c/code\u003e 和 \u003ccode class=\"literal\"\u003enot in recovery\u003c/code\u003e。\u003c/p\u003e","key":"description","title":"描述"},{"html":"\u003cdiv class=\"variablelist\"\u003e\u003cdl class=\"variablelist\"\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003cem class=\"replaceable\"\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e指定要等待的目标 \u003cacronym\u003eLSN\u003c/acronym\u003e。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eWITH ( \u003cem class=\"replaceable\"\u003e\u003ccode\u003eoption\u003c/code\u003e\u003c/em\u003e [, ...] )\u003c/code\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e此子句指定等待操作的可选参数。支持以下参数：\u003c/p\u003e\u003cdiv class=\"variablelist\"\u003e\u003cdl class=\"variablelist\"\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eMODE\u003c/code\u003e '\u003cem class=\"replaceable\"\u003e\u003ccode\u003emode\u003c/code\u003e\u003c/em\u003e'\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e指定要等待的 LSN 处理类型。如果未指定，默认值为 \u003ccode class=\"literal\"\u003estandby_replay\u003c/code\u003e。有效模式如下：\u003c/p\u003e\u003cdiv class=\"itemizedlist\"\u003e\u003cul class=\"itemizedlist\"\u003e\u003cli class=\"listitem\"\u003e\u003cp\u003e\u003ccode class=\"literal\"\u003estandby_replay\u003c/code\u003e：等待该 LSN 在备库上被重放（应用到数据库）。成功完成后，\u003ccode class=\"function\"\u003epg_last_wal_replay_lsn()\u003c/code\u003e 将返回大于或等于目标 LSN 的值。此模式只能在恢复期间使用。\u003c/p\u003e\u003c/li\u003e\u003cli class=\"listitem\"\u003e\u003cp\u003e\u003ccode class=\"literal\"\u003estandby_write\u003c/code\u003e：等待包含该 LSN 的 WAL 被写入备库磁盘，但不一定已经刷盘。这比 \u003ccode class=\"literal\"\u003estandby_flush\u003c/code\u003e 更快，但持久性保证更弱，因为数据可能仍在操作系统缓冲区中。备库上已有的 WAL（来自基础备份、归档恢复或先前的流式传输）以及从主库新接收的 WAL 均可满足此条件。此模式只能在恢复期间使用。\u003c/p\u003e\u003c/li\u003e\u003cli class=\"listitem\"\u003e\u003cp\u003e\u003ccode class=\"literal\"\u003estandby_flush\u003c/code\u003e：等待包含该 LSN 的 WAL 在备库上刷盘。这提供了持久性保证，而无需等待 WAL 被应用。备库上已有的 WAL（来自基础备份、归档恢复或先前的流式传输）以及从主库新接收的 WAL 均可满足此条件。此模式只能在恢复期间使用。\u003c/p\u003e\u003c/li\u003e\u003cli class=\"listitem\"\u003e\u003cp\u003e\u003ccode class=\"literal\"\u003eprimary_flush\u003c/code\u003e：等待包含该 LSN 的 WAL 在主库上刷盘。成功完成后，\u003ccode class=\"function\"\u003epg_current_wal_flush_lsn()\u003c/code\u003e 将返回大于或等于目标 LSN 的值。此模式只能在主库上使用（不能在恢复期间使用）。\u003c/p\u003e\u003c/li\u003e\u003c/ul\u003e\u003c/div\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eTIMEOUT\u003c/code\u003e '\u003cem class=\"replaceable\"\u003e\u003ccode\u003etimeout\u003c/code\u003e\u003c/em\u003e'\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e在指定且 \u003cem class=\"parameter\"\u003e\u003ccode\u003etimeout\u003c/code\u003e\u003c/em\u003e 大于零时，命令会一直等待到 \u003cem class=\"parameter\"\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e 达到，或者直到指定的 \u003cem class=\"parameter\"\u003e\u003ccode\u003etimeout\u003c/code\u003e\u003c/em\u003e 到期为止。值为零（默认值）表示命令会无限期等待。\u003c/p\u003e\u003cp\u003e\u003cem class=\"parameter\"\u003e\u003ccode\u003etimeout\u003c/code\u003e\u003c/em\u003e 是以毫秒为单位的时长。也可以将其指定为一个字符串，其中包含数值及其后的时间单位（见\u003ca href=\"/docs/devel/config-setting.html#CONFIG-SETTING-NAMES-VALUES\" title=\"19.1.1. 参数名称和值\"\u003e第 19.1.1 节\u003c/a\u003e）。最大值为 \u003ccode class=\"literal\"\u003e2147483647 ms\u003c/code\u003e。\u003c/p\u003e\u003cp\u003e小数值会舍入到最接近的毫秒数。因此，注意半毫秒或更短的 \u003cem class=\"parameter\"\u003e\u003ccode\u003etimeout\u003c/code\u003e\u003c/em\u003e 会向下舍入为零，这意味着无限期等待。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003eNO_THROW\u003c/code\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e指定在超时或在主库上运行时不抛出错误。在这种情况下，可以从返回值中获取结果状态。\u003c/p\u003e\u003cp\u003e当 \u003ccode class=\"literal\"\u003etimeout\u003c/code\u003e 或 \u003ccode class=\"literal\"\u003enot in recovery\u003c/code\u003e 是应用程序预期并打算处理的结果时，可使用此选项，例如重试等待、报告复制延迟，或为后续操作选择另一台服务器。命令随后会将结果作为状态返回；应用程序必须检查该状态，才能认定已达到目标 \u003cacronym\u003eLSN\u003c/acronym\u003e。如果应用程序必须在达到目标 \u003cacronym\u003eLSN\u003c/acronym\u003e 后才能继续，则应省略此选项，这样等待未成功时就会通过错误停止正常执行。\u003c/p\u003e\u003cp\u003e返回状态也会使显式事务保持可用；如果不使用此选项，相应错误会要求先回滚事务，或回滚到保存点，才能发出后续命令。\u003c/p\u003e\u003cp\u003e此选项只改变 \u003ccode class=\"literal\"\u003etimeout\u003c/code\u003e 和 \u003ccode class=\"literal\"\u003enot in recovery\u003c/code\u003e 的报告方式。其他错误仍会抛出，包括无效输入（例如格式错误的 \u003cacronym\u003eLSN\u003c/acronym\u003e 或无法识别的选项值），以及等待开始前检查的所有条件（例如在恢复期间请求 \u003ccode class=\"literal\"\u003eprimary_flush\u003c/code\u003e，或在持有锁的同时等待备库 \u003cacronym\u003eLSN\u003c/acronym\u003e）。此选项也不限制等待时长；要限制时长，应指定 \u003ccode class=\"literal\"\u003eTIMEOUT\u003c/code\u003e。\u003c/p\u003e\u003c/dd\u003e\u003c/dl\u003e\u003c/div\u003e\u003c/dd\u003e\u003c/dl\u003e\u003c/div\u003e","key":"parameters","title":"参数"},{"html":"\u003cdiv class=\"variablelist\"\u003e\u003cdl class=\"variablelist\"\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003esuccess\u003c/code\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e此返回值表示已成功达到目标 \u003cem class=\"parameter\"\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003etimeout\u003c/code\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e此返回值表示在达到目标 \u003cem class=\"parameter\"\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e 之前发生了超时。\u003c/p\u003e\u003c/dd\u003e\u003cdt\u003e\u003cspan class=\"term\"\u003e\u003ccode class=\"literal\"\u003enot in recovery\u003c/code\u003e\u003c/span\u003e\u003c/dt\u003e\u003cdd\u003e\u003cp\u003e此返回值表示数据库服务器不处于恢复状态。这可能意味着数据库服务器在接收到命令时并不处于恢复状态（即在主库上执行），也可能意味着它在达到目标 \u003cem class=\"parameter\"\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e 之前被提升。在提升的情况下，此状态表示发生了时间线变更，应用程序应重新评估目标 LSN 是否仍然适用。\u003c/p\u003e\u003c/dd\u003e\u003c/dl\u003e\u003c/div\u003e","key":"outputs","title":"输出"},{"html":"\u003cp\u003e\u003ccode class=\"command\"\u003eWAIT\u003c/code\u003e 必须作为顶层命令执行，不能从函数、过程或 \u003ccode class=\"command\"\u003eDO\u003c/code\u003e 块中执行。当前事务持有快照时也不能执行。\u003ccode class=\"command\"\u003eWAIT\u003c/code\u003e 本身不获取快照，因此可以在 \u003ccode class=\"literal\"\u003eREPEATABLE READ\u003c/code\u003e 或 \u003ccode class=\"literal\"\u003eSERIALIZABLE\u003c/code\u003e 事务中第一条获取快照的语句之前运行，但不能在该语句之后运行；无论处于何种隔离级别，如果游标或导出的快照使事务持有快照，也不能运行。此处持有的快照可能延迟重放，而 \u003ccode class=\"literal\"\u003estandby_replay\u003c/code\u003e 会等待重放，其他备库模式最终也可能需要等待重放。这也是 \u003ccode class=\"command\"\u003eWAIT\u003c/code\u003e 被实现为命令而不是函数或过程的原因，因为函数和过程执行时会持有快照。\u003c/p\u003e\u003cp\u003e恢复进行期间，如果会话已经持有锁，且尚未达到目标 \u003cem class=\"parameter\"\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e，则会拒绝 \u003ccode class=\"literal\"\u003estandby_replay\u003c/code\u003e（默认值）、\u003ccode class=\"literal\"\u003estandby_write\u003c/code\u003e 或 \u003ccode class=\"literal\"\u003estandby_flush\u003c/code\u003e 模式的等待。这样的锁可能使启动进程直接或通过另一个会话等待当前会话，而当前会话又在等待启动进程推进恢复。这个循环在当前会话一侧不涉及锁等待，因而死锁检测无法发现，也没有机制可以打破它。如果已经达到目标，等待会立即返回，因此始终允许。\u003c/p\u003e\u003cp\u003e应在事务块之外发出 \u003ccode class=\"command\"\u003eWAIT FOR\u003c/code\u003e，或将其作为事务块的第一条语句，在运行任何会获取锁的操作之前执行。这也是下文示例所示读己之写模式的自然顺序：先等待目标 \u003cem class=\"parameter\"\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e，再运行必须看到相应更改的查询。注意，在 \u003ccode class=\"literal\"\u003eREAD COMMITTED\u003c/code\u003e 下，即使先前语句的快照已经释放，它获取的锁仍会保留。因此，即使隔离级别允许等待，在这类语句之后执行等待仍会被拒绝。\u003c/p\u003e\u003cp\u003e此限制也适用于 \u003ccode class=\"literal\"\u003estandby_write\u003c/code\u003e 和 \u003ccode class=\"literal\"\u003estandby_flush\u003c/code\u003e，尽管流复制可以在不依赖启动进程的情况下推进这两个位置。这两个位置都不会落后于重放位置，因此当 walreceiver 未处于活动状态时，重放可能是推进它们的唯一途径。如果所持有的锁阻塞了重放，会话就在等待重放推进，而重放又在等待会话释放锁。在流复制下，只有 WAL 持续到达时，这些位置才会独立推进。如果接收在达到目标之前停止，被阻塞的启动进程就无法重新启动 walreceiver。它也无法重放推进重启点和回收 WAL 所需的较新检查点记录，因此 \u003ccode class=\"filename\"\u003epg_wal\u003c/code\u003e 可能被填满，导致接收在达到目标之前停止。所以，即使等待开始时流式传输处于活动状态，此限制也仍然适用。\u003c/p\u003e\u003cp\u003e\u003ccode class=\"command\"\u003eWAIT\u003c/code\u003e 会根据指定的 \u003cem class=\"parameter\"\u003e\u003ccode\u003emode\u003c/code\u003e\u003c/em\u003e 一直等待直到达到指定的 \u003cem class=\"parameter\"\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e。\u003ccode class=\"literal\"\u003estandby_replay\u003c/code\u003e 模式会等待该 LSN 被重放（应用到数据库），这有助于在使用异步副本读取和主库写入时实现读己之写一致性，前提是目标 \u003cacronym\u003eLSN\u003c/acronym\u003e 位于主库上相关写事务的 \u003ccode class=\"literal\"\u003eCOMMIT\u003c/code\u003e 记录末尾或其后。\u003ccode class=\"literal\"\u003estandby_flush\u003c/code\u003e 模式会等待 WAL 在副本上刷入持久存储，或者等待备库上已有的 WAL 已被重放。\u003ccode class=\"literal\"\u003estandby_write\u003c/code\u003e 模式会等待 WAL 被写入操作系统，或者已经重放；对于新接收的 WAL，这比刷盘更快，但持久性保证更弱。\u003ccode class=\"literal\"\u003eprimary_flush\u003c/code\u003e 模式会等待 WAL 在主库上刷盘。在所有情况下，最后一次修改的 \u003cacronym\u003eLSN\u003c/acronym\u003e 应当存储在客户端应用程序一侧或连接池一侧。\u003c/p\u003e\u003cp\u003e备库模式（\u003ccode class=\"literal\"\u003estandby_replay\u003c/code\u003e、\u003ccode class=\"literal\"\u003estandby_write\u003c/code\u003e、\u003ccode class=\"literal\"\u003estandby_flush\u003c/code\u003e）只能在恢复期间使用，而 \u003ccode class=\"literal\"\u003eprimary_flush\u003c/code\u003e 只能在主库上使用。对当前服务器状态使用错误的模式会导致错误。如果在使用备库模式等待期间备库被提升，命令将返回 \u003ccode class=\"literal\"\u003enot in recovery\u003c/code\u003e（或者在未指定 \u003ccode class=\"literal\"\u003eNO_THROW\u003c/code\u003e 时抛出错误）。提升会创建新的时间线，而正在等待的 LSN 可能指向旧时间线中的 WAL。\u003c/p\u003e\u003cp\u003e\u003ccode class=\"command\"\u003eWAIT\u003c/code\u003e只比较\u003cacronym\u003eLSN\u003c/acronym\u003e数值；它并不知道 WAL 记录属于哪条时间线。当备库跨上游时间线切换继续恢复时，这一点很重要—例如，级联备库的上游被提升。在这种情况下，只要所选等待模式使用的位置达到或超过该\u003cacronym\u003eLSN\u003c/acronym\u003e数值，\u003ccode class=\"command\"\u003eWAIT\u003c/code\u003e就会返回\u003ccode class=\"literal\"\u003esuccess\u003c/code\u003e，而不考虑该 \u003cacronym\u003eLSN\u003c/acronym\u003e属于哪条时间线。需要确认目标指向预期时间线的应用程序必须自行验证时间线。\u003c/p\u003e\u003cp\u003e在备库服务器上，\u003ccode class=\"command\"\u003eWAIT\u003c/code\u003e 会话可能会被恢复冲突中断。某些恢复冲突是不可避免的：例如，重放一个表空间删除操作时，无论后端正在执行什么，都会通过终止所有后端来解决冲突。在备库上使用 \u003ccode class=\"command\"\u003eWAIT\u003c/code\u003e 的应用程序应准备好处理此类中断，例如通过重试命令或回退到其他机制。\u003c/p\u003e","key":"notes","title":"注解"},{"html":"\u003cp\u003e可以使用 \u003ccode class=\"command\"\u003eWAIT\u003c/code\u003e 命令等待 \u003ccode class=\"type\"\u003epg_lsn\u003c/code\u003e 值。例如，应用程序可以更新 \u003ccode class=\"literal\"\u003emovie\u003c/code\u003e 表，并获取位于相关写事务的 \u003ccode class=\"literal\"\u003eCOMMIT\u003c/code\u003e 记录末尾或其后的 \u003cacronym\u003elsn\u003c/acronym\u003e。在这里展示的默认自动提交模式下，\u003ccode class=\"command\"\u003eUPDATE\u003c/code\u003e 会在后续的 \u003ccode class=\"command\"\u003eSELECT\u003c/code\u003e 之前提交。由于 \u003ccode class=\"varname\"\u003esynchronous_commit\u003c/code\u003e 可能设置为 \u003ccode class=\"literal\"\u003eoff\u003c/code\u003e，此示例在主库上使用 \u003ccode class=\"function\"\u003epg_current_wal_insert_lsn\u003c/code\u003e 获取 \u003cacronym\u003elsn\u003c/acronym\u003e。\u003c/p\u003e\u003cpre class=\"programlisting\"\u003epostgres=# UPDATE movie SET genre = 'Dramatic' WHERE genre = 'Drama';\nUPDATE 100\npostgres=# SELECT pg_current_wal_insert_lsn();\n pg_current_wal_insert_lsn\n---------------------------\n 0/0306EE20\n(1 row)\n\u003c/pre\u003e\u003cp\u003e然后，应用程序可以使用提交之后从主库获得的 \u003cem class=\"parameter\"\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e 运行 \u003ccode class=\"command\"\u003eWAIT\u003c/code\u003e。之后，主库上所做的更改应当保证在副本上可见。\u003c/p\u003e\u003cpre class=\"programlisting\"\u003epostgres=# WAIT FOR LSN '0/0306EE20';\n status\n---------\n success\n(1 row)\npostgres=# SELECT * FROM movie WHERE genre = 'Drama';\n genre\n-------\n(0 rows)\n\u003c/pre\u003e\u003cp\u003e等待刷盘（数据在副本上持久化）：\u003c/p\u003e\u003cpre class=\"programlisting\"\u003epostgres=# WAIT FOR LSN '0/0306EE20' WITH (MODE 'standby_flush');\n status\n---------\n success\n(1 row)\n\u003c/pre\u003e\u003cp\u003e带超时的等待写入：\u003c/p\u003e\u003cpre class=\"programlisting\"\u003epostgres=# WAIT FOR LSN '0/0306EE20' WITH (MODE 'standby_write', TIMEOUT '100ms', NO_THROW);\n status\n---------\n success\n(1 row)\n\u003c/pre\u003e\u003cp\u003e等待主库刷盘：\u003c/p\u003e\u003cpre class=\"programlisting\"\u003epostgres=# WAIT FOR LSN '0/0306EE20' WITH (MODE 'primary_flush');\n status\n---------\n success\n(1 row)\n\u003c/pre\u003e\u003cp\u003e如果在超时之前未达到目标 LSN，则会抛出错误：\u003c/p\u003e\u003cpre class=\"programlisting\"\u003epostgres=# WAIT FOR LSN '0/0306EE20' WITH (TIMEOUT '0.1s');\nERROR:  timed out while waiting for target LSN 0/0306EE20 to be replayed; current standby_replay LSN 0/0306EA60\n\u003c/pre\u003e\u003cp\u003e同一个示例使用带 \u003cem class=\"parameter\"\u003e\u003ccode\u003eNO_THROW\u003c/code\u003e\u003c/em\u003e 选项的 \u003ccode class=\"command\"\u003eWAIT\u003c/code\u003e：\u003c/p\u003e\u003cpre class=\"programlisting\"\u003epostgres=# WAIT FOR LSN '0/0306EE20' WITH (TIMEOUT '100ms', NO_THROW);\n status\n---------\n timeout\n(1 row)\n\u003c/pre\u003e","key":"examples","title":"示例"}],"sections_same_as":"","synopsis_html":"WAIT FOR LSN '\u003cem class=\"replaceable\"\u003e\u003ccode\u003elsn\u003c/code\u003e\u003c/em\u003e'\n    [ WITH ( \u003cem class=\"replaceable\"\u003e\u003ccode\u003eoption\u003c/code\u003e\u003c/em\u003e [, ...] ) ]\n\n\u003cspan class=\"phrase\"\u003e其中 \u003cem class=\"replaceable\"\u003e\u003ccode\u003eoption\u003c/code\u003e\u003c/em\u003e 可以是：\u003c/span\u003e\n\n    MODE '\u003cem class=\"replaceable\"\u003e\u003ccode\u003emode\u003c/code\u003e\u003c/em\u003e'\n    TIMEOUT '\u003cem class=\"replaceable\"\u003e\u003ccode\u003etimeout\u003c/code\u003e\u003c/em\u003e'\n    NO_THROW\n\n\u003cspan class=\"phrase\"\u003e而 \u003cem class=\"replaceable\"\u003e\u003ccode\u003emode\u003c/code\u003e\u003c/em\u003e 可以是：\u003c/span\u003e\n\n    standby_replay | standby_write | standby_flush | primary_flush","synopsis_text":"WAIT FOR LSN 'lsn'\n[ WITH ( option [, ...] ) ]\n\n其中 option 可以是：\n\nMODE 'mode'\nTIMEOUT 'timeout'\nNO_THROW\n\n而 mode 可以是：\n\nstandby_replay | standby_write | standby_flush | primary_flush"}},"RequestedLocale":"en","Fallback":true,"Versions":["19","20"],"Locales":["zh-Hans"],"Signatures":null,"Spellings":null,"SQLState":null,"Evidence":null}
