Skip to content

synchronous_standby_names ​

synchronous_standby_names 是同步复制的「开关」参数。它回答两个问题:等谁(哪些备库算数)和等几个(需要几个备库确认)。上一节的 synchronous_commit 只回答「等到哪一步」,本参数才决定「同步复制是否成立」。

它的值是一段有特定语法的字符串,包含三种写法:FIRST num_sync (standby_name, ...)、ANY num_sync (standby_name, ...),以及省略关键字的简写 standby_name, ...。前两种分别对应「优先级模式」与「quorum 模式」,是理解本参数的核心。

一个非常关键的运维事实是:本参数为 sighup 级,通过 pg_reload_conf() 即可生效,不需要重启。这让「临时摘除同步备库以解除写阻塞」成为一个可以在线执行的应急操作,而不需要付出重启数据库的代价。

另一个需要牢记的点是:备库名的匹配依赖 application_name。如果备库没有正确设置 application_name,那么无论主库这边写什么名字都可能匹配不上,结果是同步复制「配置了但不生效」。这是本参数最常见的配置故障。

前置知识

建议先阅读:synchronous_commit 配置

基础概念 ​

在讲语法之前,先把几个反复出现的术语固定下来。这些术语在 pg_stat_replication 的 sync_state 列里会直接出现,理解它们才能读懂监控输出。

术语含义
num_sync需要多少个备库确认才算提交成功
standby_name备库的名字,通常对应备库的 application_name
FIRST优先级模式,按列表顺序取前 num_sync 个可用的
ANYquorum 模式,列表中任意 num_sync 个备库确认即可
sync当前真正承担同步确认的备库
potentialFIRST 模式下排在其后、随时可补位的备库
async完全不参与同步确认的备库
quorumANY 模式下的同步备库角色

同步复制成立的两个条件 ​

同步复制的成立需要同时满足三个条件,缺一不可。把它们列清楚,可以避免「配了但没生效」的困惑:

条件检查位置
synchronous_standby_names 非空SHOW synchronous_standby_names;
至少有一个备库匹配到列表中的名字pg_stat_replication.application_name
备库连接状态为 streamingpg_stat_replication.state

任何一条不满足,同步复制都不会实际发生。其中第二条最容易被忽略:即使主库写了 synchronous_standby_names = 'standby1',如果备库的 application_name 是默认的 walreceiver,两者就对不上,备库会被归入 async,同步复制静默失效。

备库名从哪来 ​

备库的 standby_name 来源有明确优先级。PostgreSQL 按下面的顺序确定一个 walsender 对应的名字:

优先级来源说明
1application_name备库在 primary_conninfo 中设置的 application_name
2cluster_name若 application_name 为空,则使用备库的 cluster_name
3walreceiver若两者都为空,则为固定的 walreceiver

因此最可靠的实践是:在备库的 primary_conninfo 中显式设置 application_name,不要依赖默认值。这样主库的 synchronous_standby_names 才能稳定匹配。

conf
# 备库 postgresql.conf
primary_conninfo = 'host=10.0.0.10 port=5432 user=repl password=*** application_name=standby1'

名字匹配的大小写规则 ​

standby_name 的匹配是大小写不敏感的。也就是说,主库写 standby1、备库设 Standby1,仍然可以匹配上。这一规则同样适用于 application_name 的比较。

但这并不意味着可以随意混用大小写。包含特殊字符或需要保留大小写含义的名字必须用双引号括起来,就像 SQL 标识符的规则一样。不加引号时,名字会被折叠为小写。

conf
# 不加引号:名字会被规范化为小写
synchronous_standby_names = 'standby1'

# 含空格或需保留原样:必须用双引号
synchronous_standby_names = '"standby 1"'

空值就是异步 ​

synchronous_standby_names 的默认值是空字符串。空值意味着没有任何同步备库,此时实例处于异步复制模式,synchronous_commit 的备库相关档位全部退化为本地等待。

这一点之所以重要,是因为它让「关闭同步复制」变成了一个极低成本的应急动作:只要把本参数置空并 reload,所有被同步复制卡住的写操作会立刻恢复。这也是本页后面「应急摘除」一节的基础。

语法与用法 ​

本参数支持三种写法,分别对应不同的语义。

写法一:FIRST(优先级模式)

conf
# 需要 1 个备库确认,按顺序优先 standby1,不可用时用 standby2
synchronous_standby_names = 'FIRST 1 (standby1, standby2)'

# 需要 2 个备库确认(列表前两个可用的)
synchronous_standby_names = 'FIRST 2 (standby1, standby2, standby3)'

写法二:ANY(quorum 模式,PostgreSQL 10 引入)

conf
# 列表中任意 2 个备库确认即可,不要求特定的备库
synchronous_standby_names = 'ANY 2 (standby1, standby2, standby3)'

# 任意 1 个备库确认即可
synchronous_standby_names = 'ANY 1 (standby1, standby2, standby3)'

写法三:省略关键字的简写

conf
# 等价于 FIRST 1 (standby1, standby2)
synchronous_standby_names = 'standby1, standby2'

# 等价于 FIRST 1 (任意名字) —— 通配所有备库
synchronous_standby_names = '*'

* 是一个特殊值,表示「匹配任意备库名」。使用 * 时,任何已连接的备库都可能被选为同步备库,具体选谁由 sync_priority 决定。

生效方式 ​

sql
-- 在线修改(无需重启)
ALTER SYSTEM SET synchronous_standby_names = 'FIRST 1 (standby1, standby2)';
SELECT pg_reload_conf();

-- 确认生效
SHOW synchronous_standby_names;

也可以直接改 postgresql.conf 后执行 pg_ctl reload 或 SELECT pg_reload_conf();。两者的区别在于,ALTER SYSTEM 写入的是 postgresql.auto.conf,优先级高于 postgresql.conf。

查看当前同步备库状态 ​

sql
SELECT application_name, client_addr, state, sync_state, sync_priority
FROM pg_stat_replication
ORDER BY sync_priority DESC, application_name;
text
 application_name | client_addr |   state   | sync_state | sync_priority
------------------+-------------+-----------+------------+---------------
 standby1         | 10.0.0.11   | streaming | sync       |             1
 standby2         | 10.0.0.12   | streaming | potential  |             2
 analytics        | 10.0.0.13   | streaming | async      |             0
(3 rows)

详细说明 ​

FIRST 与 ANY 的语义差异 ​

FIRST 与 ANY 是两种截然不同的设计取向,理解差异是选型的关键。

维度FIRST(优先级)ANY(quorum)
选择方式按列表顺序,取前 num_sync 个可用的任意 num_sync 个确认即可
是否保证特定备库收到是(优先列表中靠前的)否(不保证某一个具体备库收到)
备库故障时的行为顺延到列表中的下一个可用备库用剩余可用备库凑够 num_sync
适用场景有明确的主备层级(如同一机房优先)多副本对等,追求更高可用性
引入版本早期版本PostgreSQL 10

一个形象的比喻是:FIRST 像「按值班表叫人」,叫不到 A 就叫 B、再叫不到就叫 C;ANY 像「在大厅里喊一声,谁先应算谁」。

这个差异在实际运维中的表现是:使用 FIRST 时,可以预期「确认提交的总是机房内那台低延迟备库」;使用 ANY 时,确认提交的可能是任何一台,包括跨机房的高延迟备库,因此延迟表现可能不稳定。

FIRST 的顺延行为 ​

FIRST 模式下,PostgreSQL 会把列表中的备库按顺序「填位」:先尝试让第一个成为 sync,如果它不可用(断连或落后),则把第二个提上来成为 sync,原来的第二个变为 sync,后续依此类推。

这一点可以用 pg_stat_replication 直观验证:

text
-- standby1 断开后
 application_name | sync_state | sync_priority
------------------+------------+---------------
 standby2         | sync       |             2
 standby3         | potential  |             3

standby1 消失后,standby2 自动变成了 sync。整个过程不需要人工干预,也不会阻塞写入(前提是列表里还有可用的备库)。

这个顺延能力是 FIRST 模式的重要价值:它让「主备切换后的同步复制」能够自动衔接。但要注意,顺延的前提是「列表里还有其他可用备库」。如果列表里只有 standby1 一个,那么它断连时同步复制会直接变成写阻塞。

num_sync 与备库数量的关系 ​

num_sync 与列表中备库的数量之间有一个必须满足的关系,违反它会造成严重后果:

情况结果
num_sync ≤ 可用备库数正常,提交按预期等待确认
num_sync > 可用备库数提交永久阻塞,写操作全部卡住

同步备库数不足会导致提交永久阻塞

如果把 FIRST 2 (standby1, standby2) 中的 standby2 停掉,而 standby1 也恰好不可用,那么没有任何备库能凑够 num_sync = 2,主库上的所有写事务会无限期阻塞在 SyncRep 等待上,直到有备库恢复或参数被改回。这不是超时,而是真正的「永久等待」,因为 PostgreSQL 的设计目标是「宁可不返回,也不违反同步承诺」。

应急恢复手段:执行 ALTER SYSTEM SET synchronous_standby_names = ''; 然后 SELECT pg_reload_conf();,把同步复制临时关闭,写操作会立刻恢复;等备库修好后再把参数改回来。

因此 num_sync 的设置必须结合「有多少备库可以在故障时顶上」来考虑。如果只有两台备库,把 num_sync 设为 2 意味着「任何一台备库不可用,主库就写不动」——这是非常脆弱的配置。

* 通配符的用法与风险 ​

synchronous_standby_names = '*' 表示匹配任意备库名,等价于 FIRST 1 (*)。它的优点是配置简单,不需要逐个维护备库名;缺点是任何连上来的备库都会被纳入同步候选,包括临时的分析库或测试库。

这在多租户或环境混用的机器上是一个隐患:某个临时从主库拉流做测试的实例,可能会因为 * 被选为同步备库,从而影响主库的提交延迟;如果它随后断连,还可能引发写阻塞。

因此 * 只建议用在「备库集合固定、且都应由主库同步保护」的场景。生产环境更推荐显式列出备库名。

同步与异步混合部署 ​

一个实例上可以同时存在同步备库与异步备库,这对读写分离架构非常有用:用同步备库保证不丢数据,用异步备库承接只读查询(避免只读查询拖慢主库提交)。

conf
# 只把 standby1、standby2 纳入同步候选,analytics 保持异步
synchronous_standby_names = 'FIRST 1 (standby1, standby2)'

pg_stat_replication.sync_state 的四种取值正好描述了这种混合局面:

sync_state含义是否影响提交
sync当前承担同步确认的备库是
potentialFIRST 模式下可补位的备库否(除非被提升为 sync)
async异步备库否
quorumANY 模式下已确认的同步备库是

在 FIRST 1 (s1, s2) 的情形下,s1 是 sync、s2 是 potential,两者都在同步列表里,但只有 s1 实际参与当前确认;把 s1 停掉,s2 会自动变成 sync。

sync_priority 的意义 ​

sync_priority 表示备库在同步列表中的优先级。它的取值规则是:

情况sync_priority
在 FIRST 列表中,排在第一位正整数,越小越优先(按列表位置编号)
在 FIRST 列表中,排在后面依次递增
异步备库0(不参与同步)

在 ANY(quorum)模式下,sync_priority 通常都是 0,因为 quorum 不关心顺序。因此不要用 sync_priority 去判断 quorum 模式下的同步状态,应该看 sync_state 是否为 quorum。

一个实用的监控规则是:只要存在 sync_state = 'sync' 的备库,就说明同步复制在正常工作;如果所有备库都是 async,说明同步复制实际未生效,需要立刻排查备库名匹配问题。

备库名匹配不上时的表现 ​

备库名匹配失败是同步复制最常见的故障,其表现具有隐蔽性:没有任何报错,只是同步复制静默失效。

典型症状:

  • synchronous_standby_names 的值看起来是对的;
  • pg_stat_replication 里有备库在 streaming,但 sync_state 全是 async;
  • 主库的提交延迟没有变化(说明根本没在等备库);
  • 用 SHOW synchronous_standby_names; 确认参数已生效。

排查方法是对比两边的名字:

sql
-- 主库侧:看备库实际报到主库的名字
SELECT application_name, client_addr, sync_state
FROM pg_stat_replication;
text
 application_name | client_addr | sync_state
------------------+-------------+------------
 walreceiver      | 10.0.0.11   | async
(1 row)

上面的输出中,备库的名字是默认的 walreceiver,而主库配置的是 standby1,两者不匹配,所以 sync_state 是 async。修复方式是到备库上修改 primary_conninfo:

conf
# 备库 postgresql.conf
primary_conninfo = 'host=10.0.0.10 port=5432 user=repl application_name=standby1'

改完后需要重启备库(primary_conninfo 是 postgresql.conf 参数,备库侧修改后需重启恢复进程)或在备库上执行 SELECT pg_reload_conf();。

写阻塞的排查与恢复 ​

当同步备库不可用导致写入阻塞时,快速恢复是第一优先级。一个标准的排查与处置流程如下:

sql
-- 第一步:确认是否有大量会话在等同步复制
SELECT count(*) AS waiting_sessions, min(query_start) AS oldest
FROM pg_stat_activity
WHERE wait_event = 'SyncRep';
text
 waiting_sessions |            oldest
------------------+-------------------------------
              218 | 2026-09-30 10:12:41.220+08
(1 row)
sql
-- 第二步:确认同步备库状态(是否还有 sync)
SELECT application_name, state, sync_state,
       pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS replay_lag_bytes
FROM pg_stat_replication;
text
 application_name |   state   | sync_state | replay_lag_bytes
------------------+-----------+------------+------------------
 standby1         | streaming | async      |          918273645
(1 row)

第三步——如果确认同步备库已不可用且业务写入已受影响,临时摘除:

sql
ALTER SYSTEM SET synchronous_standby_names = '';
SELECT pg_reload_conf();

执行后,等待 SyncRep 的会话会立刻被唤醒,写入恢复。需要注意的是,这是一次数据保护等级的临时降级:在此期间提交的事务没有同步备库保护,主库崩溃仍可能丢数据。因此必须在故障排除后立刻改回,并记录这次降级的时间窗口。

如果希望「暂时只摘掉故障的那一台」,也可以把参数改成只包含健康备库的列表:

sql
-- 从 FIRST 1 (standby1, standby2) 改成 FIRST 1 (standby2)
ALTER SYSTEM SET synchronous_standby_names = 'FIRST 1 (standby2)';
SELECT pg_reload_conf();

这种方式比整体置空更精细,保留了同步保护,是在「还有健康备库」时应该优先采用的处置方式。

与复制槽的配合 ​

复制槽与同步复制是两个正交的机制,常常一起使用:

机制解决的问题
复制槽主库不会在备库收到之前回收 WAL,避免备库断连后需要重建
同步复制提交时等待备库确认,主库故障时不丢已提交事务

一个常见的组合配置是:为每个备库建一个物理复制槽,同时用 synchronous_standby_names 指定同步备库。这样既能避免备库短暂断连导致的 WAL 断档,又能保证提交的持久性。

需要注意,复制槽由备库通过 primary_slot_name 指定:

conf
# 备库 postgresql.conf
primary_conninfo = 'host=10.0.0.10 port=5432 user=repl application_name=standby1'
primary_slot_name = 'standby1_slot'

复制槽的存在会让主库「等到备库收到为止」地保留 WAL,这在同步复制失效(例如被临时置空)时提供了一个额外缓冲:即使同步复制被摘除,只要备库最终恢复连接,仍有可能通过复制槽追上进度。

与 Patroni 的配合 ​

使用 Patroni 时,synchronous_standby_names 通常不要手工维护。Patroni 会根据集群状态自动生成这个参数,其行为由配置项控制:

配置项作用
synchronous_mode开启同步复制,选中一个同步备库
synchronous_mode_strict严格模式,不自动降级为异步
synchronous_node_count同步备库数量,对应 num_sync

Patroni 的做法是:先选出一个延迟最低的备库,把它写进 synchronous_standby_names,并把其他备库列为候选。当该备库故障时,Patroni 会自动改写参数切换到下一个候选。

因此在使用 Patroni 的环境中,手工 ALTER SYSTEM SET synchronous_standby_names 往往会被 Patroni 覆盖。正确的调整方式是修改 Patroni 的配置文件并重启 Patroni 进程。判断参数是否被托管,可以观察 synchronous_standby_names 是否在备库切换时自动变化——如果会自动变,说明有工具在管理。

多备库下的选型建议 ​

不同规模下,synchronous_standby_names 的推荐写法不同:

场景推荐写法理由
一主一备FIRST 1 (standby1)简单明确,但备库故障即写阻塞
一主两备FIRST 1 (standby1, standby2)有顺延能力,单备库故障不阻塞
一主三备(同机房优先)FIRST 1 (local1, local2, remote1)优先同机房低延迟
一主三备(对等)ANY 2 (s1, s2, s3)任意两个确认,容错性更强
备库动态增减* 或 FIRST 1 (*)免维护,但需确保备库集合可控

一个经验法则是:num_sync 不要等于备库总数。留出至少一个余量,才能在单台备库故障时不阻塞写入。例如三台备库配 ANY 2,可以容忍任意一台故障而写入不受影响;配 ANY 3 则任何一台故障都会导致写阻塞。

参数变化的可见性 ​

调整同步备库列表后,可以立即从几个地方验证效果:

观察点期望
SHOW synchronous_standby_names;返回最新值
pg_stat_replication.sync_state出现 sync(或 quorum)
pg_stat_activity.wait_event = 'SyncRep'出现等待(说明确实在等)
应用侧提交延迟相应上升(这是同步生效的代价)

如果前三项都对但第四项没变化,需要确认应用连接是否复用了旧连接(synchronous_commit 是 user 级参数,老连接不会自动更新),或者备库的 fsync 太快导致延迟增量被噪声淹没。

实战示例 ​

示例一:一主两备的完整配置 ​

主库(postgresql.conf):

conf
# 主库配置
listen_addresses = '*'
wal_level = replica
max_wal_senders = 10
wal_keep_size = 1GB
synchronous_commit = on
synchronous_standby_names = 'FIRST 1 (standby1, standby2)'

主库的复制授权(pg_hba.conf):

conf
# 允许复制用户从备库网段连接
host    replication     repl    10.0.0.0/24    scram-sha-256

备库(postgresql.conf):

conf
# 备库 1
primary_conninfo = 'host=10.0.0.10 port=5432 user=repl password=*** application_name=standby1'
primary_slot_name = 'standby1_slot'
hot_standby = on
conf
# 备库 2
primary_conninfo = 'host=10.0.0.10 port=5432 user=repl password=*** application_name=standby2'
primary_slot_name = 'standby2_slot'
hot_standby = on

验证结果应该形如:

sql
SELECT application_name, sync_state, sync_priority, state
FROM pg_stat_replication ORDER BY sync_priority DESC;
text
 application_name | sync_state | sync_priority |   state
------------------+------------+---------------+-----------
 standby1         | sync       |             1 | streaming
 standby2         | potential  |             2 | streaming
(2 rows)

示例二:模拟备库故障时的顺延 ​

先把 standby1 停掉:

bash
# 在备库 1 上停止实例
pg_ctl -D /var/lib/pgsql/18/data stop -m fast

在主库上观察:

sql
SELECT application_name, sync_state, sync_priority
FROM pg_stat_replication ORDER BY sync_priority DESC;
text
 application_name | sync_state | sync_priority
------------------+------------+---------------
 standby2         | sync       |             2
(1 row)

standby2 从 potential 变成了 sync,写入没有被阻塞。这就是 FIRST 模式顺延能力的价值:只要列表里还有备库,单台故障不会影响可用性。

示例三:模拟 num_sync 过大导致的写阻塞 ​

把参数改成需要两个备库确认,然后停掉其中一台:

sql
-- 主库:需要 2 个确认,但只有 2 台备库
ALTER SYSTEM SET synchronous_standby_names = 'FIRST 2 (standby1, standby2)';
SELECT pg_reload_conf();
bash
# 停掉 standby1
pg_ctl -D /var/lib/pgsql/18/data stop -m fast

此时在主库执行写入:

sql
INSERT INTO t_probe(id, ts) VALUES (1, now());

这条语句会一直挂起不返回。用另一个会话查看:

sql
SELECT pid, state, wait_event_type, wait_event, query
FROM pg_stat_activity
WHERE wait_event = 'SyncRep';
text
  pid  | state  | wait_event_type | wait_event |              query
-------+--------+-----------------+------------+----------------------------------
 21044 | active | IPC             | SyncRep    | INSERT INTO t_probe(id, ts) ...
(1 row)

这正是「num_sync 大于可用备库数导致永久阻塞」。恢复方式:

sql
ALTER SYSTEM SET synchronous_standby_names = 'FIRST 1 (standby1, standby2)';
SELECT pg_reload_conf();

改回 num_sync = 1 后,挂起的 INSERT 会立刻完成。

示例四:应急摘除同步复制 ​

sql
-- 记录当前值与时间,便于事后恢复
SELECT current_setting('synchronous_standby_names') AS before_removal;
text
        before_removal
------------------------------
 FIRST 1 (standby1, standby2)
(1 row)
sql
-- 临时置空,解除写阻塞
ALTER SYSTEM SET synchronous_standby_names = '';
SELECT pg_reload_conf();

-- 确认已生效
SHOW synchronous_standby_names;
text
 synchronous_standby_names
---------------------------
 
(1 row)

确认写入恢复:

sql
SELECT count(*) FROM pg_stat_activity WHERE wait_event = 'SyncRep';
text
 count
-------
     0
(1 row)

故障排除后,务必恢复同步保护:

sql
ALTER SYSTEM SET synchronous_standby_names = 'FIRST 1 (standby1, standby2)';
SELECT pg_reload_conf();

示例五:排查备库名不匹配 ​

sql
-- 主库配置的名字
SHOW synchronous_standby_names;
text
 synchronous_standby_names
---------------------------
 FIRST 1 (standby1)
(1 row)
sql
-- 备库实际报到的名字
SELECT application_name, sync_state FROM pg_stat_replication;
text
 application_name | sync_state
------------------+------------
 walreceiver      | async
(1 row)

两边名字不一致,导致 sync_state 是 async,同步复制没有生效。在备库上修正 primary_conninfo 中的 application_name 后重启备库恢复进程即可。

示例六:用 ANY 配置 quorum 并观察状态 ​

sql
ALTER SYSTEM SET synchronous_standby_names = 'ANY 2 (standby1, standby2, standby3)';
SELECT pg_reload_conf();
sql
SELECT application_name, state, sync_state
FROM pg_stat_replication
ORDER BY application_name;
text
 application_name |   state   | sync_state
------------------+-----------+------------
 standby1         | streaming | quorum
 standby2         | streaming | quorum
 standby3         | streaming | quorum
(3 rows)

在 ANY 模式下,参与同步的备库 sync_state 显示为 quorum,而非 sync/potential。这是区分 FIRST 与 ANY 的一个直观标志。

注意事项 ​

备库名必须有对应的 application_name。 这是本参数第一大故障源。备库没有正确设置 application_name 时,主库配置再正确也无法匹配,且不会有任何报错。

num_sync 大于可用备库数会导致永久写阻塞。 这不是超时或重试,而是真正的无限等待。设计 num_sync 时必须留出至少一台备库的余量。

本参数是 sighup 级,可以 reload 生效,无需重启。 这使得在线应急摘除同步复制成为可能,应把这条命令写进应急手册。

改动 synchronous_standby_names 会影响正在等待的后端。 把备库从列表中移除会让正在等待该备库确认的事务立刻被唤醒(因为不再需要等它),这是应急处置能立即生效的原因;反过来,把备库加入列表可能让后续提交开始变慢。

FIRST 与 ANY 不要混写在同一个值里。 两种写法互斥,必须二选一。写错会导致参数解析失败或语义不符预期。

含有特殊字符的备库名要加双引号。 与 SQL 标识符规则一致,不加引号时名字会被规范化为小写,可能导致匹配失败。

* 通配符会把所有备库纳入候选。 在有临时实例连入主库的环境中,这可能带来意料之外的同步备库与提交延迟。

延迟备库不要放进同步列表。 设置了 recovery_min_apply_delay 的备库如果在同步列表中且档位是 remote_apply,会让主库提交等待整个延迟时间,等价于数据库写死。

托管方案下不要手工改这个参数。 Patroni 等工具会自动覆盖它,手工修改会导致「改了又变回去」,应通过工具的配置项调整。

监控 sync_state 而不是只看参数值。 参数非空不等于同步生效,唯一可靠的判断是 pg_stat_replication 中存在 sync 或 quorum 状态的备库。

最佳实践 ​

  1. 为每个备库显式设置 application_name。 不要依赖 cluster_name 或默认的 walreceiver,显式命名是避免匹配失败最有效的手段。

  2. num_sync 永远留出至少一台余量。 三台备库配 ANY 2 或 FIRST 1,都能容忍单台故障;配到与备库数相同则非常脆弱。

  3. 优先使用 FIRST 以获得可预期的延迟。 如果备库分布在不同机房,FIRST 可以确保确认来自低延迟的那一台;ANY 的延迟不确定性更高。

  4. 把同步备库按网络延迟排序。 在 FIRST 列表中,把同机房、低延迟的备库放前面,跨机房的放后面作为后备。

  5. 同时配置复制槽与同步复制。 复制槽保证断连后能续上,同步复制保证不丢提交,两者互补。

  6. 把「临时置空 synchronous_standby_names」写进应急手册。 同时写清恢复命令与「事后必须改回」的检查项,避免应急后忘记恢复导致长期无保护运行。

  7. 对 sync_state 消失设置告警。 当实例上「存在同步备库」变成「全部为 async」时,说明同步复制已经失效,这应当是一个高优先级告警。

  8. 对 SyncRep 等待会话数设置告警。 等待数突然升高是同步备库变慢或掉线的第一信号;等待数长期不为零则说明同步复制成了常态瓶颈。

  9. 定期在演练环境模拟单台备库故障。 验证顺延是否按预期发生、写入是否被阻塞,把「num_sync 是否留了余量」变成一次实测而不是纸上推演。

  10. 使用托管方案时,在工具配置中声明同步需求。 不要在 PostgreSQL 层面手工维护 synchronous_standby_names,避免与自动化逻辑互相打架。

  11. 变更同步备库列表时同步更新监控与文档。 备库名、num_sync、模式(FIRST/ANY)都应记录在架构文档里,避免后续排查时靠猜。

  12. 不要用 * 作为长期配置。 它适合动态环境或临时场景;稳态生产环境应显式列出备库名,避免临时实例意外成为同步备库。

参考链接 ​