4. 基础备份与pg_basebackup

在 PostgreSQL 的高可用体系中,基础备份(Base Backup)是流复制的起点。它把主库在某个时刻的整个数据目录复制出来,再配合 WAL 归档或流复制,就能把备库推进到与主库一致的状态。理解基础备份的关键,不是“复制文件”这个动作本身,而是它如何保证备份期间数据库的一致性。

一致性备份原理

PostgreSQL 的数据文件在运行中始终处于被修改的状态,直接复制数据目录会得到“撕裂”的页面和互相矛盾的元组。为此,PostgreSQL 采用“在线备份”机制:备份开始时记录一个起始 WAL 位置(Start LSN),备份结束时记录结束 WAL 位置(Stop LSN),并在这段时间内保证所有被修改的数据块在 WAL 中都有完整记录。恢复时,先还原数据目录,再重放从 Start LSN 到 Stop LSN 的 WAL,就能把数据文件“补齐”到一致状态。

pg_basebackup 正是这一机制的命令行实现。它通过复制协议连接主库,触发一次在线备份,把数据目录打包传输到本地或远端。备份过程中,主库会进入备份模式(或使用复制槽),确保 WAL 不被过早回收。因此,pg_basebackup 得到的备份天然包含恢复所需的一致性信息,只要 WAL 完整,就能恢复出一个可用的实例。

pg_basebackup 一致性备份与恢复链路 主库 Primary 数据目录 $PGDATA WAL 日志 复制槽 / 备份模式 pg_basebackup 过程 Start LSN → 复制数据文件 → Stop LSN tar / plain 格式输出 备库 Standby 还原数据目录 standby.signal primary_conninfo WAL 流复制 / 归档 Start LSN → Stop LSN 重放 一致性保证:备份期间所有数据块变更写入 WAL;恢复时重放 WAL 补齐数据文件,使备库达到与主库一致的状态。 关键参数:-D 目标目录、-h 主机、-p 端口、-U 用户、-F 格式、-X 方式、-P 进度、-R 写恢复配置、-S 复制槽。

pg_basebackup 参数详解

pg_basebackup 的参数决定了备份的形态、传输方式和恢复便利性。以下是最常用的一组参数及其工程含义:

参数含义工程建议
-D, --pgdata备份输出目录必须为空目录;备库场景直接指向 $PGDATA
-h, --host主库主机名或 IP使用可解析的主机名,便于切换
-p, --port主库端口默认 5432,多实例时显式指定
-U, --username连接用户需具备 REPLICATION 或 SUPERUSER 权限
-F, --format输出格式:plain / tar备库直连用 plain;归档传输用 tar
-X, --wal-methodWAL 包含方式:none / fetch / stream推荐 stream,边备份边传 WAL
-P, --progress显示进度大库备份时观察进度,便于排障
-R, --write-recovery-conf自动生成 standby.signal 和 primary_conninfo备库场景强烈建议开启
-S, --slot使用复制槽避免 WAL 被过早回收,需主库预先创建
-c, --checkpoint检查点模式:fast / spreadfast 立即触发检查点,缩短备份窗口

其中 -X stream 与 -R 的组合最值得关注。-X stream 会在备份数据文件的同时,通过第二个连接实时传输 WAL,避免备份结束后还要单独去主库拉取 WAL;-R 则自动在目标目录写入 standby.signal 和 primary_conninfo,让备库启动后直接进入流复制状态,省去手工配置。

备份到备库的完整操作流程

下面以“主库 192.168.1.10:5432,备库 192.168.1.11,数据目录 /var/lib/pgsql/15/data”为例,演示一次完整的备库搭建。以下参数与路径均为教学示例,实际执行时请按环境调整。

第一步:主库准备复制用户与复制槽。复制用户需要具备 REPLICATION 权限;复制槽用于保证备份期间及后续流复制的 WAL 不被回收。

-- 在主库执行
CREATE ROLE repl_user WITH REPLICATION LOGIN PASSWORD 'Repl@123';
SELECT * FROM pg_create_physical_replication_slot('standby_slot');

第二步:确认主库允许复制连接。检查 pg_hba.conf 中是否有针对备库 IP 的 replication 条目,并确认 listen_addresses 与 wal_level 满足要求(wal_level = replica 或更高)。

# pg_hba.conf 示例
host    replication     repl_user    192.168.1.11/32    scram-sha-256

# postgresql.conf 关键项
listen_addresses = '*'
wal_level = replica
max_wal_senders = 10

第三步:在备库执行 pg_basebackup。先停止备库实例,清空目标数据目录,再执行备份命令。注意 -D 指向的目录必须为空或不存在。

# 在备库执行
systemctl stop postgresql-15
rm -rf /var/lib/pgsql/15/data/*
chown postgres:postgres /var/lib/pgsql/15/data

pg_basebackup \
  -h 192.168.1.10 \
  -p 5432 \
  -U repl_user \
  -D /var/lib/pgsql/15/data \
  -F plain \
  -X stream \
  -S standby_slot \
  -P \
  -R \
  -c fast

执行过程中,-P 会输出类似 12345/67890 kB (12%) 的进度信息;-X stream 会同时传输 WAL;-R 会在数据目录生成 standby.signal 和 postgresql.auto.conf 中的 primary_conninfo。

第四步:启动备库并验证。启动备库后,通过 pg_stat_replication 和 pg_last_wal_receive_lsn() 观察复制状态。

# 启动备库
systemctl start postgresql-15

# 在主库查看复制状态
SELECT client_addr, state, sync_state, sent_lsn, replay_lsn
FROM pg_stat_replication;

# 在备库确认只读与接收位置
SELECT pg_is_in_recovery();
SELECT pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn();

若 state 显示 streaming,且 replay_lsn 持续追赶 sent_lsn,说明备库已进入正常流复制状态。

常见错误与处理

错误 1:pg_basebackup: error: could not connect to server。通常是 pg_hba.conf 未放行备库 IP,或主库 listen_addresses 未监听对应地址。处理:检查主库 pg_hba.conf 的 replication 条目,确认 listen_addresses 包含备库可达的地址,并重载配置。

错误 2:FATAL: no pg_hba.conf entry for replication connection。复制连接走的是 replication 关键字,不是普通 database 条目。处理:在 pg_hba.conf 中显式添加 host replication repl_user 备库IP/32 scram-sha-256。

错误 3:ERROR: replication slot "standby_slot" does not exist。复制槽未创建或名称拼写错误。处理:在主库执行 SELECT * FROM pg_create_physical_replication_slot('standby_slot');,并确认 -S 参数与槽名一致。

错误 4:pg_basebackup: directory "/var/lib/pgsql/15/data" exists but is not empty。目标目录非空。处理:停止备库,清空目录后再执行;切勿在运行中的实例目录上直接覆盖。

错误 5:备份完成后备库无法启动,日志提示 could not find WAL segment。通常是 -X 使用了 none 或 fetch 但 WAL 未完整保留。处理:改用 -X stream 并配合复制槽,确保备份期间 WAL 不丢失。

排障时优先看三处:主库 pg_hba.conf 是否放行 replication、复制槽是否存在、备库数据目录是否为空。这三项覆盖了绝大多数 pg_basebackup 失败场景。

掌握 pg_basebackup 的一致性原理与参数组合后,备库搭建就从“手工拷贝 + 反复试错”变成了可重复、可验证的标准操作。下一步只需把复制槽、连接信息和监控指标固化到部署脚本中,就能稳定地批量扩展备库。