Skip to content

主库配置(wal_level / max_wal_senders) ​

要让一台 PostgreSQL 实例成为「能对外提供 WAL 的发送方」,需要在主库上完成四项配置:提高 WAL 记录级别、开放足够的 walsender 名额、让监听地址对外可达、创建一个只用于复制的账号并放行 pg_hba.conf。这四项缺任何一项,备库都无法建立复制连接,而且失败原因往往分散在日志与网络层,排查起来很费时间。

本页把主库侧的配置按「必须改的」和「按需改的」两类讲清楚。必须改的是 wal_level、max_wal_senders、listen_addresses 和复制账号授权;按需改的是 wal_keep_size、max_replication_slots、max_slot_wal_keep_size、wal_sender_timeout、synchronous_standby_names。之所以要区分,是因为前者涉及重启窗口,后者多数可以在线调整,规划变更时的风险完全不同。

有一个前提需要反复强调:主库配置的参数在备库提升为主库之后依然生效,含义不变。这些参数描述的是「发送方的能力」,而不是「这台机器是不是主库」。因此把同样的配置保留在所有节点上,会让故障切换后的行为更可预测。本页所有示例都假定运行 PostgreSQL 18。

前置知识

建议先阅读:流复制原理

基础概念 ​

主库侧配置的核心目标是「让 WAL 足够完整、让发送通道足够宽、让连接能被授权」。下表汇总了这些参数的角色与生效方式。

参数默认值生效方式是否必须调整
wal_levelreplica重启是(若当前为 minimal)
max_wal_senders10重启是(若为 0 或名额不足)
listen_addresseslocalhost重启是(备库不在本机时)
wal_keep_size0reload按需(不使用复制槽时)
max_replication_slots10重启按需(使用复制槽且数量较多时)
max_slot_wal_keep_size-1reload推荐调整(防磁盘撑爆)
wal_sender_timeout60sreload一般保持默认
synchronous_standby_names空reload按需(要 RPO=0 时)

wal_level 的三档含义 ​

wal_level 决定写入 WAL 的信息量,也就是「WAL 里记录了多少可用于重建的数据」。它有三个取值:

取值记录内容支持的复制方式典型用途
minimal只记录崩溃恢复所需的最小信息不支持任何复制单机、可容忍重做备份的批处理
replica记录足够重建数据页的信息(含物理复制所需)物理复制、PITR 归档生产默认、流复制
logical在 replica 基础上额外记录逻辑解码所需信息(如旧值)物理复制 + 逻辑复制需要逻辑解码 / 逻辑订阅

logical 与 replica 的关系是包含:logical 能做 replica 能做的一切,代价是 WAL 体积更大。生产上如果没有逻辑复制需求,用 replica(默认值)即可。

需要特别记住 minimal 的两个「坑」:第一,minimal 模式下部分操作(ALTER TABLE ... SET TABLESPACE、CLUSTER、CREATE TABLE、REFRESH MATERIALIZED VIEW(不带 CONCURRENTLY)、REINDEX、TRUNCATE)会走「不写 WAL」的优化路径,因为它假设重放时会重新执行整条命令而非逐页复制;第二,max_wal_senders 非零时服务器无法在 minimal 下启动,两者配置互斥。

max_wal_senders 与 walsender 名额 ​

max_wal_senders 限制同时运行的 walsender 进程数,即「同时能有多少个复制连接或流式基础备份连接」。默认 10,值为 0 表示复制被完全禁用。

需要理解两点:其一,流式基础备份(pg_basebackup 使用 -X stream 时)也会占用一个 walsender 名额,所以实际规划时要把备份工具也算进去;其二,客户端异常断开时连接槽可能被短暂占用直到超时,因此建议配置值比「预期客户端数」略高一些,让断线的客户端能立即重连而不必等待。

max_wal_senders 只能通过重启设置,这一点在扩容备库数量时容易踩坑——加备库之前必须先确认名额并对需要的情况重启。

wal_keep_size 的定位 ​

wal_keep_size 指定 pg_wal 目录中为备库保留的最小 WAL 容量,单位 MB,默认 0。要理解它的两个限定词:

  • 最小:它设定的是下限,实际保留量可能更多。系统还需要为归档或从检查点恢复保留额外的段,这些都不受此参数约束。
  • 备库用途:它只为「可能落后很多的备库」多留一些缓冲,不保证某个具体备库所需的 WAL 一定存在。

如果值为 0(默认),系统不会为备库额外保留任何 WAL,备库能取到多少历史 WAL,完全取决于「上一个检查点在哪」以及「归档状态如何」。因此在不使用复制槽的方案里,wal_keep_size 是唯一的兜底手段。

一个重要的版本事实:PostgreSQL 13 之前,这个参数叫 wal_keep_segments,单位是 WAL 段数(每段 16MB)。从 PostgreSQL 13 起它被 wal_keep_size 取代,单位改为 MB。关键词是「取代」——wal_keep_size 不是 wal_keep_segments 的别名,在新版本文档里不应该再提 wal_keep_segments。

max_replication_slots 与复制槽上限 ​

max_replication_slots 指定服务器能支持的复制槽数量上限,默认 10,只能在启动时设置。两个使用要点:

  • 设置成低于当前已有槽数的值,会导致服务器无法启动。缩容前必须先删除多余槽。
  • wal_level 必须为 replica 或更高,复制槽才能被使用。

如果集群里既有物理复制槽(给备库)又有逻辑复制槽(给下游订阅),它们共享这个上限,规划时要一起算。

synchronous_standby_names 概览 ​

synchronous_standby_names 是打开同步复制的开关,留空表示不使用同步复制。它的语法有两种模式:

ini
# FIRST 模式:按优先级选出前 num_sync 个作为同步备库
synchronous_standby_names = 'FIRST 1 (standby1, standby2)'

# ANY 模式:从列表里任选 num_sync 个确认即可(quorum 语义)
synchronous_standby_names = 'ANY 2 (standby1, standby2, standby3)'

备库名来自备库 primary_conninfo 里的 application_name(若未设置,则默认取备库的 cluster_name,都没有时回退为 walreceiver)。因此「给每台备库设置唯一的 application_name」是配置同步复制的前置条件。

这个参数只能是「主库在等备库」,方向不可逆;且它无法跨越级联复制——同步复制要求备库直连主库,级联中的下游备库不能被声明为同步备库。

listen_addresses 与复制可达性 ​

listen_addresses 决定 PostgreSQL 监听哪些网络接口,默认 localhost(只监听回环地址)。备库在另一台机器上时,必须让主库监听对外的网卡地址或使用 *:

ini
listen_addresses = '*'

同时相关的还有 port(默认 5432)。监听地址修改需要重启。修改后务必用 ss -lntp 之类的命令确认端口真的在对外监听,而不是只改了配置文件却忘了重启。

wal_sender_timeout 的作用 ​

wal_sender_timeout 是主库判定「备库已经失活」的超时时间,默认 60 秒。walsender 在超过这段时间没有收到备库的任何回复(包括心跳)时,会终止这条复制连接,并在日志里记录一条类似 terminating walsender process due to replication timeout 的消息。

值为 0 会禁用超时机制。禁用之后,一条半死的连接(例如网络中断但没有 FIN 包)会一直挂在那里,占用 walsender 名额,因此除非有明确理由,不建议设为 0。备库侧对应的参数是 wal_receiver_timeout,一般保持两侧接近。

pg_hba.conf 的 replication 条目 ​

复制连接的鉴权与普通连接走同一套 pg_hba.conf,但数据库字段必须写 replication——这是复制协议使用的伪数据库名。写成具体的库名不会匹配,表现为备库连接被拒绝。

ini
# TYPE   DATABASE      USER   ADDRESS           METHOD
# 允许 192.168.1.0/24 网段用 repl 账号做复制
host     replication   repl   192.168.1.0/24    scram-sha-256

规则顺序很重要:pg_hba.conf 是自上而下第一条匹配即生效,所以不要把宽泛的 all all all trust 之类的条目放在复制条目之前把它盖掉。修改后需要 reload 生效(pg_ctl reload 或 SELECT pg_reload_conf())。

CREATE ROLE ... REPLICATION ​

复制账号只需要 REPLICATION 与 LOGIN 两个属性,不需要超级用户。赋予 REPLICATION 之后,该角色可以发起复制连接、也能读取全部 WAL(等价于能读全库数据),所以密码强度必须按高敏感凭据对待。

sql
CREATE ROLE repl WITH REPLICATION LOGIN PASSWORD 'Ch4ngeM3_Str0ng';

REPLICATION 是一个独立的角色属性,与 SUPERUSER、CREATEDB、CREATEROLE 并列。查看已有复制角色:

sql
SELECT rolname, rolreplication, rolcanlogin
FROM pg_roles
WHERE rolreplication
ORDER BY rolname;

主库侧配置的检查清单 ​

在动手配置备库之前,用下面这张清单把主库确认一遍,可以避免把主库问题误判为备库问题。

检查项期望结果
SHOW wal_levelreplica 或 logical
SHOW max_wal_senders大于实际备库数 + 备份客户端数
SHOW listen_addresses包含备库可访问的地址
复制角色存在pg_roles.rolreplication = true
pg_hba.conf 有 replication 条目数据库字段为 replication
复制端口可达从备库 psql replication=database 能成功
pg_replication_slots规划中的槽已创建(若使用槽)

语法与用法 ​

主库配置本身就是「改 postgresql.conf + 改 pg_hba.conf + 执行 SQL」三件事。下面给出可照抄的模板。

postgresql.conf 主库侧模板:

ini
# ---------- 必需项(修改后需要重启) ----------
wal_level = replica                  # 流复制最低要求
max_wal_senders = 10                 # 并发 walsender 数,至少为备库数
max_replication_slots = 10           # 复制槽数量上限(使用槽时)
listen_addresses = '*'               # 允许备库访问
port = 5432

# ---------- 建议项(reload 生效) ----------
wal_keep_size = 512                  # 不使用复制槽时的兜底保留量(MB)
max_slot_wal_keep_size = 10GB        # 单槽允许保留的 WAL 上限,防止撑爆磁盘
wal_sender_timeout = 60s             # 备库失活判定超时

# ---------- 可选:异步为默认,开启同步复制时再配置 ----------
# synchronous_standby_names = 'FIRST 1 (standby1)'

pg_hba.conf 模板:

ini
# 复制连接:DATABASE 字段必须为 replication
host    replication    repl    192.168.1.20/32    scram-sha-256
host    replication    repl    192.168.1.21/32    scram-sha-256

创建复制角色并验证:

sql
CREATE ROLE repl WITH REPLICATION LOGIN PASSWORD 'Ch4ngeM3_Str0ng';
bash
# 从主库本机测试复制连接(IDENTIFY_SYSTEM 是复制协议命令)
psql "host=192.168.1.10 port=5432 user=repl dbname=postgres replication=database" \
     -c "IDENTIFY_SYSTEM;"

创建物理复制槽:

sql
SELECT pg_create_physical_replication_slot('standby1_slot');

让配置生效:

bash
# 只改了可 reload 参数
pg_ctl reload -D /var/lib/postgresql/18/main

# 改了 wal_level / max_wal_senders / listen_addresses 时必须重启
pg_ctl restart -D /var/lib/postgresql/18/main

用 SQL 确认实际生效值(尤其是确认 pending_restart):

sql
SELECT name, setting, unit, pending_restart
FROM pg_settings
WHERE name IN ('wal_level', 'max_wal_senders', 'max_replication_slots',
               'wal_keep_size', 'max_slot_wal_keep_size', 'wal_sender_timeout')
ORDER BY name;
text
           name           | setting | unit | pending_restart
--------------------------+---------+------+-----------------
 max_replication_slots    | 10      |      | f
 max_slot_wal_keep_size   | 10240   | MB   | f
 max_wal_senders          | 10      |      | f
 wal_keep_size            | 512     | MB   | f
 wal_level                | replica |      | f
 wal_sender_timeout       | 60000   | ms   | f
(6 rows)

详细说明 ​

wal_level 为什么必须至少是 replica ​

WAL 是「重放即得数据」的日志。要让另一台机器只靠 WAL 重建出与主库逐字节一致的页面,WAL 里必须包含足够的信息。replica 级别保证了两件事:一是数据页变更以「整页镜像 + 行级变更」的形式被完整记录(配合全页写入机制,见WAL 写流程);二是记录粒度细到足以支持任意时刻的崩溃恢复。

minimal 之所以不够,是因为它做了一些「假设重放时会重新执行整条命令」的优化:例如 CREATE TABLE 在 minimal 下可能只记录「发生了建表」,而依赖重放端自己重新执行。这种依赖重放端「再跑一遍逻辑」的方式无法保证与主库物理页一致,因此不能支撑物理复制。

结论:只要打算做流复制,就把 wal_level 设为 replica(或 logical)。这是所有复制能力的第一块基石,也是唯一一个「改了就必须重启」的 WAL 相关参数。

修改 wal_level 的正确顺序 ​

wal_level 的修改有连带影响,尤其是从高改低的时候。正确顺序是:

  1. 确认当前没有依赖高 WAL 级别的功能在跑(归档、复制、逻辑解码);
  2. 修改 wal_level;
  3. 重启实例;
  4. 重新做一次基础备份(从高降到低时尤其重要)。

之所以降级后要重做备份,是因为降低 wal_level 会让先前基于较高 wal_level 产生的 WAL 与新产生的 WAL 在信息量上不一致,用旧备份 + 新 WAL 恢复可能缺少必要信息。反过来,从 minimal 升到 replica 同样建议重做备份,因为升级前的 WAL 缺少复制所需信息。

计算 max_wal_senders 的合理值 ​

max_wal_senders 的规划需要把三类消费者都算上:

消费者占用名额
每个流复制备库1
每个级联的上游(备库向更下游发送)该备库自身需要配置,主库侧不占
每次流式基础备份(pg_basebackup -X stream)1
备用余量(断线重连窗口)建议 2 以上

一个常见的经验公式是「预期同时连接数 + 2」。例:3 台备库 + 偶尔做基础备份,可设为 6~8。设得太紧的典型症状是:一台备库重启后主库日志报 no free replication slots(注意这条消息说的是 walsender 名额,不是复制槽),备库连不上直到某条旧连接超时释放。

复制槽与 wal_keep_size 的取舍 ​

不使用复制槽时,主库无法知道每台备库的进度,只能用 wal_keep_size 保底。这种方案的弱点很明显:保留容量是按「最落后的备库」的假设来定的,如果某台备库停了很久,它需要的 WAL 可能已经被回收。

使用复制槽后,主库为每台备库精确记账,代价是槽会无限制保留 WAL。因此两种方案的风险方向相反:

方案主要风险缓解手段
只用 wal_keep_size备库落后过多导致 WAL 缺失、复制中断加大 wal_keep_size、配置归档
只用复制槽备库长期不连导致 pg_wal 撑爆磁盘max_slot_wal_keep_size、idle_replication_slot_timeout
槽 + 归档 + 上限(推荐)—三者互补

生产上的稳健组合是:使用复制槽保证不误删 WAL,同时配置 max_slot_wal_keep_size 给磁盘设上限,另外保留 WAL 归档作为「一旦截断还能补齐」的后路。复制槽的创建与维护参见复制槽。

wal_keep_size 与归档的配合 ​

wal_keep_size 只影响 pg_wal 里保留多少段,不影响归档。也就是说,即便 wal_keep_size = 0,只要归档链路正常,主库在段写满时仍会先调用 archive_command 归档,归档成功后才允许回收。

因此备库在 WAL 缺失时,如果没有复制槽,可以从归档目录里补齐(前提是配置了 restore_command)。这就是「流复制 + 归档」组合的额外价值:它给复制中断提供了一条恢复路径,而不必立刻重做基础备份。归档与检查点的关系参见WAL 与检查点最佳实践。

max_slot_wal_keep_size 的行为 ​

max_slot_wal_keep_size 限制单个复制槽允许保留的 WAL 容量,单位 MB,默认 -1(无限)。当某个槽的 restart_lsn 落后当前 LSN 超过这个值时,主库会在检查点删除那些「只被该槽需要」的 WAL 段。

这带来的后果是:使用该槽的备库可能因为所需 WAL 被删除而无法继续复制,必须重做基础备份。这是一个「主动选择丢掉备库、保住主库磁盘」的取舍——相比让主库磁盘写满导致全库不可用,牺牲一台备库显然是更好的选择。

槽的 WAL 可用性可以通过 pg_replication_slots.wal_status 观察,取值包括 reserved(充足)、extended、unreserved、lost。当出现 lost 时,该槽对应的备库已确定无法继续,需要重建。

复制槽相关视图的读法 ​

主库上判断「谁在用哪个槽、落后多少」主要看两个视图:

sql
SELECT slot_name, slot_type, active, active_pid,
       restart_lsn, confirmed_flush_lsn
FROM pg_replication_slots
ORDER BY slot_name;
text
   slot_name   | slot_type | active | active_pid | restart_lsn | confirmed_flush_lsn
---------------+-----------+--------+------------+-------------+---------------------
 standby1_slot | physical  | t      |      12345 | 0/2FF8D18   |
 standby2_slot | physical  | f      |            | 0/2F00120   |
(1 row)
  • active = f 的物理槽表示备库当前没连上;如果它长期不活跃,pg_wal 会因这个槽而持续增长。
  • restart_lsn 是槽「还能保证提供 WAL 的最低位置」,它与 pg_current_wal_lsn() 的差值就是该槽的落后字节数。
  • 物理槽的 confirmed_flush_lsn 通常为空,它主要用于逻辑槽。

PostgreSQL 18 引入了 idle_replication_slot_timeout,可以让长期不活跃的槽在检查点时被自动失效,从而避免「僵尸槽」无限占盘。该参数默认 0(禁用),且只能在 postgresql.conf 或命令行设置。

reload 与 restart 的边界 ​

主库侧参数分两类,改错类别会导致「以为改好了其实没生效」。

参数生效方式备注
wal_level重启不能用 pg_reload_conf()
max_wal_senders重启只能启动时设置
max_replication_slots重启只能启动时设置
listen_addresses重启涉及 socket 重建
port重启同上
wal_keep_sizereload只能在 postgresql.conf 或命令行设置
max_slot_wal_keep_sizereload只能在 postgresql.conf 或命令行设置
idle_replication_slot_timeoutreload只能在 postgresql.conf 或命令行设置
wal_sender_timeoutreload用户/超级用户上下文
synchronous_standby_namesreload不能在 postgresql.auto.conf 中设置

注意最后一条的细节:synchronous_standby_names 和 wal_keep_size 这类参数被限定为「只能在 postgresql.conf 或命令行设置」,意味着用 ALTER SYSTEM SET 写进 postgresql.auto.conf 会被视为无效(而不是报错),这点很容易被忽略。用 pg_settings.pending_restart 可以判断「是否有参数改了但还没重启生效」:

sql
SELECT name, setting, pending_restart
FROM pg_settings
WHERE pending_restart;
text
     name     | setting | pending_restart
--------------+---------+-----------------
 wal_level    | replica | t
(1 row)

pending_restart = t 表示该参数已修改但需要重启才能生效——这是一个非常值得纳入日常巡检的信号。

复制认证方式的选择 ​

pg_hba.conf 的 METHOD 决定了复制连接的认证方式。常见选择:

方法说明适用场景
scram-sha-256强口令认证(默认推荐)跨主机复制的标准选择
md5旧式口令认证PostgreSQL 18 已弃用,应迁移
cert客户端证书认证有 PKI 体系时可用,安全性高
trust无认证放行禁止用于复制连接
reject明确拒绝用于兜底规则

trust 绝不应该出现在复制条目里:它等于把「读取全部 WAL」的权限对网络敞开。PostgreSQL 18 已经弃用 MD5 密码(CREATE ROLE / ALTER ROLE 设置 MD5 密码时会发出弃用警告),新部署应直接用 scram-sha-256。

主库侧的容量与性能影响 ​

开启流复制会给主库带来几项可度量的开销:

  1. WAL 体积增大:wal_level 从 minimal 升到 replica 会增加 WAL 写入量,进而影响磁盘 I/O 与归档量。
  2. 全页写入开销:为保证备库与崩溃恢复可用,检查点后首次修改的页面会写整页镜像,这会放大 WAL 体积(见全页写入)。
  3. walsender CPU 与网络:每个 walsender 需要读取 WAL 并序列化发送,备库数量多时 CPU 与出口带宽是可观开销。
  4. 复制槽引发的 WAL 驻留:槽越落后,pg_wal 越大,占用磁盘并可能影响检查点效率。

因此在规划主库容量时,应把 WAL 目录的磁盘空间、网络出口带宽、以及 walsender 的 CPU 开销一并算进去,而不是只按「单机数据库」估算。

实战示例 ​

示例一:从默认配置改造为可复制的主库 ​

假设主库当前是默认配置(wal_keep_size = 0、listen_addresses = localhost),需要改造成可对外复制。

bash
# 1. 备份原配置
cp /var/lib/postgresql/18/main/postgresql.conf \
   /var/lib/postgresql/18/main/postgresql.conf.bak
ini
# 2. 编辑 postgresql.conf,加入以下几行
wal_level = replica
max_wal_senders = 10
max_replication_slots = 10
wal_keep_size = 512
max_slot_wal_keep_size = 10GB
listen_addresses = '*'
wal_sender_timeout = 60s
bash
# 3. 重启使需要重启的参数生效
pg_ctl restart -D /var/lib/postgresql/18/main
sql
-- 4. 确认生效
SELECT name, setting, unit, pending_restart
FROM pg_settings
WHERE name IN ('wal_level','max_wal_senders','wal_keep_size','listen_addresses');
text
        name        | setting | unit | pending_restart
--------------------+---------+------+-----------------
 listen_addresses   | *       |      | f
 max_wal_senders    | 10      |      | f
 wal_keep_size      | 512     | MB   | f
 wal_level          | replica |      | f
(4 rows)

示例二:创建复制角色并放行 pg_hba ​

sql
CREATE ROLE repl WITH REPLICATION LOGIN PASSWORD 'Ch4ngeM3_Str0ng';
ini
# pg_hba.conf 追加(注意放在宽泛规则之前)
host    replication    repl    192.168.1.20/32    scram-sha-256
host    replication    repl    192.168.1.21/32    scram-sha-256
bash
# reload 让 pg_hba 生效
pg_ctl reload -D /var/lib/postgresql/18/main
sql
-- 在主库上确认角色属性
SELECT rolname, rolreplication, rolcanlogin, rolsuper
FROM pg_roles WHERE rolname = 'repl';
text
 rolname | rolreplication | rolcanlogin | rolsuper
---------+----------------+-------------+----------
 repl    | t              | t           | f
(1 row)

注意 rolsuper = f:复制角色不需要超级用户,权限越高风险越大。

示例三:用 IDENTIFY_SYSTEM 验证复制通道 ​

IDENTIFY_SYSTEM 是复制协议自带的「握手 + 自报家门」命令,用它测试可以在动备库之前把认证问题排掉。

bash
psql "host=192.168.1.10 port=5432 user=repl password=Ch4ngeM3_Str0ng \
      dbname=postgres replication=database" -c "IDENTIFY_SYSTEM;"
text
      systemid       | timeline |  xlogpos  | dbname
---------------------+----------+-----------+--------
 7230697861213143552 |        1 | 0/3000000 |
(1 row)

如果这条命令失败,常见原因与对应检查点:

报错/现象可能原因
no pg_hba.conf entry for replication connectionpg_hba.conf 缺少 replication 条目或网段不匹配
password authentication failed密码错误、password_encryption 与存储方式不一致
could not connect to serverlisten_addresses 未开放、防火墙拦截、端口错误
FATAL: replication is not enabledwal_level 为 minimal 或 max_wal_senders 为 0

示例四:创建物理复制槽并观察状态 ​

sql
SELECT pg_create_physical_replication_slot('standby1_slot');
text
 pg_create_physical_replication_slot
-------------------------------------
 (standby1_slot,0/3000000)
(1 row)
sql
-- 稍后再观察,槽在备库连上后会变为 active
SELECT slot_name, slot_type, active, restart_lsn, wal_status
FROM pg_replication_slots;
text
   slot_name   | slot_type | active | restart_lsn | wal_status
---------------+-----------+--------+-------------+------------
 standby1_slot | physical  | f      | 0/3000000   | reserved
(1 row)

wal_status = reserved 表示槽需要保留的 WAL 都还在,备库随时可以连接。若某天变成 lost,就意味着该槽需要的 WAL 已被删除,备库必须重建。

示例五:评估是否打开了同步复制 ​

如果业务要求 RPO = 0,需要配置 synchronous_standby_names。先确认备库名(即备库 primary_conninfo 中的 application_name),再设置:

ini
synchronous_standby_names = 'FIRST 1 (standby1)'
synchronous_commit = 'on'
sql
-- reload 后确认
SELECT name, setting FROM pg_settings
WHERE name IN ('synchronous_standby_names','synchronous_commit');
text
           name            |   setting
---------------------------+--------------
 synchronous_standby_names | FIRST 1 (standby1)
 synchronous_commit        | on
(1 row)
sql
-- 观察每个备库是否被识别为同步
SELECT application_name, sync_priority, sync_state, state
FROM pg_stat_replication;
text
 application_name | sync_priority | sync_state |   state
------------------+---------------+------------+-----------
 standby1         |             1 | sync       | streaming
 standby2         |             0 | potential  | streaming
(1 row)

sync_state = sync 表示该备库当前被计为同步确认者;potential 表示它是候选(在当前同步备库故障后可顶上);async 表示它不参与同步确认。同步复制的完整机制参见同步复制配置。

注意事项 ​

wal_keep_size 取代了 wal_keep_segments,不是别名。 从 PostgreSQL 13 起旧参数被移除并换成以 MB 为单位的 wal_keep_size。在 PostgreSQL 18 上不存在 wal_keep_segments,不要再按段数配置。

listen_addresses 改了不重启等于没改。 这是最隐蔽的问题之一:配置文件里写好了新值,但实例还是旧 socket,表现为备库连不上而主库自己完全正常。用 pg_settings.pending_restart 可以确认是否有未生效的参数。

pg_hba.conf 的匹配顺序是自上而下第一条命中。 如果前面有一条宽泛的规则(例如针对同网段的 all all)先命中,后面精心写的 replication 条目根本不会生效。放行范围应尽量收窄。

max_replication_slots 设得比现有槽数少会导致实例无法启动。 这是「改小参数」的经典事故。缩容的正确顺序是先删槽再改参数再重启。

max_wal_senders = 0 会彻底禁用复制。 包括 pg_basebackup 的流式备份在内,全部失败。如果有人为了「省资源」把它设为 0,任何复制能力都会消失。

synchronous_standby_names 与 wal_keep_size 不能在 postgresql.auto.conf 生效。 这两个参数被限定为「只能在 postgresql.conf 或命令行设置」,用 ALTER SYSTEM SET 写入不会生效,且不一定报错,容易造成困惑。

同步复制的备库名必须与备库 application_name 严格一致。 备库未设置 application_name 时默认值是 walreceiver(或备库的 cluster_name),如果主库按自以为的名字配置同步,结果会是「同步复制静默地不生效」——因为没有任何备库匹配上。配置完成后务必用 pg_stat_replication.sync_state 确认。

复制账号的密码是高敏感凭据。 拥有 REPLICATION 的角色可以读取全部 WAL,等价于能还原全库数据。密码要按数据库管理员账号的级别保护,不要写在明文脚本或镜像里。

开启 wal_level = logical 会显著增加 WAL 体积。 它额外记录逻辑解码所需的信息,对写入密集型系统的磁盘与带宽影响不可忽略。没有逻辑复制需求时就用 replica。

不要在主库上手工删除 pg_wal 中的文件。 无论是为了「省空间」还是「清理旧的」,手工删除都会造成 WAL 断档或复制中断。要控制占用,应通过复制槽上限、归档修复等正常手段。

最佳实践 ​

  1. 把主库配置模板化,并在所有节点保持一致。 wal_level、max_wal_senders、max_replication_slots、wal_keep_size 这类参数描述的是「发送能力」,提升后的备库同样需要,统一配置可以避免切换后行为不一致。

  2. max_wal_senders 按「备库数 + 备份客户端数 + 2」规划。 留足断线重连的余量,避免一台备库重启后长时间连不上。

  3. 优先使用复制槽,同时必须设置 max_slot_wal_keep_size。 光有槽没有上限,就是给磁盘埋雷;只设上限不用槽,则进度无法精确跟踪。两者要成对使用。

  4. 在 PostgreSQL 18 上评估启用 idle_replication_slot_timeout。 它可以自动失效长期不活跃的槽,防止僵尸槽无限占盘。注意槽失效在检查点触发,配置的时长不是精确的失效时刻。

  5. 复制账号专用、最小权限、强密码。 只授予 REPLICATION 与 LOGIN,绝不授予 SUPERUSER,认证方式使用 scram-sha-256。

  6. pg_hba.conf 复制条目按最小网段放行。 使用 /32 单机精度而不是整段 /24,并按业务需要精确到每台备库。

  7. 把「待重启参数」纳入巡检。 定期查询 pg_settings 中 pending_restart = true 的行,避免有人在事故中改了 wal_level 却忘了重启,导致复制始终失败。

  8. wal_sender_timeout 保持默认或按网络质量微调,不要设为 0。 高延迟或抖动大的跨地域链路可以适当调大;禁用超时则会让半死连接长期占用 walsender 名额。

  9. 主库同时保留 WAL 归档。 归档既支撑 PITR,也是复制 WAL 缺失时的补齐来源。归档配置参见WAL 归档配置。

  10. 给 pg_wal 目录与网络出口分别设置告警。 pg_wal 突增通常指向复制槽落后或归档故障;出口带宽饱和则说明备库数量或传输量已经超出链路承载能力。

  11. 配置同步复制前,先明确「备库不可用时主库是否接受阻塞」。 同步复制的代价是可用性与写入延迟,必须与业务方共同确认,而不是由 DBA 单方面决定。

  12. 每次修改主库复制相关参数后,都从备库侧验证一次。 主库 SHOW 出来的值只是「主库以为的」,真正的健康判定要靠备库能否连上并持续回放(见 pg_stat_replication)。

下一节

继续学习:备库配置(primary_conninfo)

参考链接 ​