副本(只读备用)
创建现有实例的只读物理副本。副本通过 PostgreSQL 物理复制从其主实例持续流式传输 WAL,并提供只读查询服务——适用于读写分离、报告或作为热备。
发生了什么
- 预检 — 主实例必须正在运行(在任何配置写入之前验证;停止的主实例会失败但没有副作用)
- 注册 — 添加新实例条目,使用主实例的数据库名称和密码(参见注释),禁用 PITR,
replica_of设置为主实例 - 复制设置 — 在主实例上:
pg_hba.conf为回环地址和 RFC1918 范围添加host replication条目(幂等)- 创建物理复制槽
pgcli_r_<name>,保留 WAL 以确保副本不会落后于 WAL 回收
- 基础备份 —
pg_basebackup -R将主实例的数据目录复制到副本的数据目录,写入primary_conninfo(带密码)和standby.signal,使副本以备用模式启动 - 启动 — 副本容器启动并持续流式传输 WAL
验证
销毁
销毁副本是两步操作:
步骤 1 必须在步骤 2 之前运行:PostgreSQL 拒绝删除仍在流式传输的槽(replication slot is active),因此必须先销毁副本以关闭其流式连接。
注意: 如果主实例是本地 pgcli 管理的实例(同一主机),可以跳过步骤 2 —
destroy会自动清理槽。当从副本主机无法访问主实例时需要步骤 2。
跨网络副本
上述同主机流程假设主实例和副本共享一个服务器(一个 podman 守护进程,一个网络)。对于在另一台主机上的副本,pgcli 在每一侧运行一个命令——无需 SSH,唯一的跨机器信息就是你作为参数传递的内容:
获取主实例 DSN
如果主实例是 pgcli 管理的实例,使用 pg status 获取其连接信息:
然后将 127.0.0.1 替换为从副本主机看到的主实例主机 IP(例如 10.241.20.50)——用户、密码和数据库按原样使用。注意主实例主机必须接受从副本主机到该端口的 TCP 连接(防火墙/安全组)。
每一侧的作用:
- 主实例侧(
--replica-host <ip|hostname>):仅准备主实例——本地不创建任何内容。它将host replication all <addr>条目追加到pg_hba.conf并创建物理槽pgcli_r_<name>,然后打印要在副本主机上运行的确切--primary-dsn命令。IP 地址获得/32(IPv6 为/128)掩码;主机名按原样写入。已在托管 RFC1918 范围(10.0.0.0/8、172.16.0.0/12、192.168.0.0/16)内的 IP 会被跳过去重。幂等——重新运行不会添加重复行。 - 副本侧(
--primary-dsn):首先验证主实例上存在槽(验证连接性和顺序——在主实例侧之前运行会失败并带有可操作的错误消息且没有副作用),然后注册实例,通过网络从 DSN 运行pg_basebackup(主机网络),并启动备用实例。
副本侧仅在副本主机上运行:副本实例的用户、数据库和密码来自 DSN(物理复制复制 pg_authid,因此本地密码必须等于主实例的密码)。主实例名称使用 --primary-name 给出并记录为 replica_of;它不必存在于副本主机的配置中,-i 不用于远程主实例——如果给出,它保持其严格含义并且必须引用真实的本地实例。
销毁是对称的,每个主机一个命令——按此顺序:
步骤 1 必须在步骤 2 之前运行:PostgreSQL 拒绝删除仍在流式传输的槽(replication slot is active),因此必须先销毁副本以关闭其流式连接。
自动槽清理: 当跨主机副本设置了 PrimaryDSN 时,destroy 会自动尝试通过 DSN 删除远程主实例上的复制槽。如果主实例可达,则无需手动步骤 2。replica drop 是幂等的——当槽已不存在时重新运行会作为无操作成功。
非 pgcli 主实例
主实例不必由 pgcli 管理——只要主实例侧已手动准备(槽检查仅验证槽是否存在,不验证谁创建了它),副本侧可以对任何 PostgreSQL 服务器工作:
- 在
pg_hba.conf中允许从副本主机复制,然后重新加载(SELECT pg_reload_conf()): - 使用确切名称
pgcli_r_<replica-name>创建物理槽(副本侧检查此名称):需要wal_level = replica(或logical)和具有REPLICATION权限的用户——DSN 用户。
然后副本侧命令不变:
销毁时,主实例侧没有 pgcli——在 pg destroy -i ro1 之后,手动删除槽:
如果基础备份失败(例如网络中断),销毁副本并重新运行副本侧命令——主实例侧的槽和 hba 条目保持有效。
注释
- 只读 — 副本拒绝所有写入(
cannot execute INSERT in a read-only transaction)。要使其可写你需要提升它(pg_ctl promote),这尚未作为 pgcli 命令暴露 - 相同数据,相同密码 — 物理复制是主实例的逐字节复制,包括
pg_authid。因此副本的管理员密码和数据库名称与主实例相同;只有容器名称、端口和数据目录不同。使用--dsn风格的连接时使用副本的端口 - 副本上禁用 PITR — 备用实例不归档任何内容,也不在 pgBackRest 备份容器中注册;备份在主实例上运行
- 主实例必须正在运行 — 初始创建(
pg_basebackup)和持续流式传输都需要;如果主实例重启,副本会自动重新连接(槽显示为active) - 延迟显示 —
replica list延迟(now() - pg_last_xact_replay_timestamp())在主实例空闲时会增长;它会在下一个复制的事务时降回零。这是预期的空闲行为,不是漂移 - 幂等启动 — 重复的
pg start -i ro1在数据目录已初始化时跳过基础备份