PostgreSQL 14 主备集群实战部署教程:流复制 + 双机 WAL 归档 + 自动化备份

目录

文章目录

  • 目录
    • [1. 架构总览](#1. 架构总览)
    • [2. 环境准备](#2. 环境准备)
    • [3. 主库配置](#3. 主库配置)
    • [4. 创建复制用户与配置 pg_hba](#4. 创建复制用户与配置 pg_hba)
    • [5. 备库搭建(一条命令)](#5. 备库搭建(一条命令))
    • [6. 流复制验证](#6. 流复制验证)
    • [7. 双机 WAL 归档设计](#7. 双机 WAL 归档设计)
      • [8.1 原理:standby 归档](#8.1 原理:standby 归档)
      • [8.2 为什么双机都归档](#8.2 为什么双机都归档)
      • [8.3 备库归档验证](#8.3 备库归档验证)
    • [8. 自动化:每日基础备份 + 7 天清理](#8. 自动化:每日基础备份 + 7 天清理)
      • [9.1 每日基础备份(备库,基于目录名保留 7 天)](#9.1 每日基础备份(备库,基于目录名保留 7 天))
      • [9.2 WAL 归档清理(备库)](#9.2 WAL 归档清理(备库))
      • [9.3 主库归档清理(同款思路)](#9.3 主库归档清理(同款思路))
    • [9. 断档保护:物理复制槽](#9. 断档保护:物理复制槽)
      • [10.1 问题背景](#10.1 问题背景)
      • [10.2 创建槽(主库)](#10.2 创建槽(主库))
      • [10.3 备库绑定槽:正确姿势](#10.3 备库绑定槽:正确姿势)
      • [10.4 槽上限保险丝](#10.4 槽上限保险丝)
      • [10.5 验证绑定成功(主库)](#10.5 验证绑定成功(主库))
    • [10. 坑点实录](#10. 坑点实录)
      • [坑 1:root 属主毁天灭地 ------ 归档目录和数据目录一个都不放过](#坑 1:root 属主毁天灭地 —— 归档目录和数据目录一个都不放过)
      • [坑 2:`primary_conninfo` 改完不重启 walreceiver 不生效](#坑 2:primary_conninfo 改完不重启 walreceiver 不生效)
      • [坑 3:备库到底能不能做 basebackup?](#坑 3:备库到底能不能做 basebackup?)
      • [坑 4:脚本漏了 cron 的 PATH](#坑 4:脚本漏了 cron 的 PATH)
    • [11. 容灾切换手册](#11. 容灾切换手册)
      • [12.1 主库故障 → 提升备库](#12.1 主库故障 → 提升备库)
      • [12.2 旧主库修好后 → 重建为新备库](#12.2 旧主库修好后 → 重建为新备库)
    • [12. 附录:日常检查命令](#12. 附录:日常检查命令)
    • 尾声

1. 架构总览

复制代码
                    ┌─────────────────────┐
                    │  主库 pg-node-1      │  192.168.24.21(读写)
                    │  /data/postgre/data  │
                    │  archive_mode = on   │
                    │  归档 → /data/postgre/archive/(gzip,保留 7 天) │
                    └──────────┬──────────┘
                               │ 流复制(异步)
                               │ 槽: standby_slot
                               │ 延迟 ~1.8ms
                    ┌──────────┴──────────┐
                    │  备库 pg-node-2      │  192.168.24.22(只读)
                    │  /data/postgre/data  │
                    │  archive_mode = always(standby 归档)
                    │  归档 → /data/backups/waldata/(gzip,保留 7 天)
                    │  每日 23:00 在备库本机做 pg_basebackup
                    │  备份 → /data/backups/wal-basic-backup/backup_YYYYMMDD_HHMMSS/
                    └─────────────────────┘

关键设计决策

决策 取舍
异步复制 容灾场景用手动切换,不追求零丢失;同步复制会拖慢每个提交
双机各自归档 主库归档保 PITR;备库独立归档(archive_mode=always),两台机器互为备份备份源
从备库做 basebackup 备份压力不落在主库,主库 io 一点不占用
每日备份 + 7 天 WAL 恢复窗口 = 7 天内的任意时间点

2. 环境准备

  • 两台 RHEL/CentOS 9 系机器(本文以 RHEL 为例),主机名 pg-node-1(主)、pg-node-2(备)
  • 内网互通:192.168.24.21192.168.24.22
  • PostgreSQL 14 从 PGDG 仓库安装
bash 复制代码
# 两台机器都执行
dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm
dnf install -y postgresql14-server postgresql14
/usr/pgsql-14/bin/postgresql-14-setup initdb
systemctl enable --now postgresql-14

数据目录默认 /var/lib/pgsql/14/data,本文统一挪到 /data/postgre/data(数据盘独立挂载,更利于规划):

bash 复制代码
systemctl stop postgresql-14
mkdir -p /data/postgre
cp -a /var/lib/pgsql/14/data /data/postgre/data
rm -rf /var/lib/pgsql/14/data
sed -i "s#/var/lib/pgsql/14/data#/data/postgre/data#g" /usr/lib/systemd/system/postgresql-14.service
systemctl daemon-reload
systemctl start postgresql-14

3. 主库配置

编辑 /data/postgre/data/postgresql.conf

ini 复制代码
# ===== 主库 pg-node-1 =====
wal_level = replica                        # 流复制至少 replica
archive_mode = on                          # 主库归档:on 即可
archive_command = 'gzip -c %p > /data/postgre/archive/%f.gz && test -f /data/postgre/archive/%f.gz'
max_wal_senders = 10                       # 至少 2:一个给流复制,一个给备份
wal_keep_size = 1024MB                     # 无槽时的兜底保留窗口
max_slot_wal_keep_size = 20GB              # 复制槽上限保险丝(见第 9 节)

归档命令拆解

  • gzip -c %p:把 WAL 文件(%p = 源文件完整路径)压缩到标准输出
  • > /data/postgre/archive/%f.gz:重定向落到归档目录(%f = WAL 文件名,如 00000001000000000000000D
  • && test -f /data/postgre/archive/%f.gz:确保文件真实存在
  • 为什么用 && 而不是 ;||&& 短路时 gzip 的错误码原样传给 PostgreSQL(PG 只认退出码);用 ; 会把退出码取成最后一条 test 的,掩盖真实错误;用 || 可能被残留文件"救活"造成假成功。

创建归档目录并修正属主权限(这步不做,就是坑点实录里那个 42 次失败的真实现场):

bash 复制代码
mkdir -p /data/postgre/archive
chown postgres:postgres /data/postgre/archive
chmod 700 /data/postgre/archive

重启使参数生效:

bash 复制代码
systemctl restart postgresql-14

4. 创建复制用户与配置 pg_hba

bash 复制代码
su - postgres -c "psql -c \"CREATE ROLE repl_user REPLICATION LOGIN PASSWORD '<REPL_PASSWORD>'\""

/data/postgre/data/pg_hba.conf 追加(按实际网段收紧):

复制代码
# replication 连接仅允许备库
host    replication     repl_user       192.168.24.22/32        md5

然后 reload:

bash 复制代码
su - postgres -c "psql -c 'SELECT pg_reload_conf()'"

5. 备库搭建(一条命令)

主库要把 pg_hba 放开给备库的主机 IP(上一步已做)。 在备库 pg-node-2 上:

bash 复制代码
# 先把备库的 postgres 服务停掉(如果初始化过)并准备目录
systemctl stop postgresql-14
rm -rf /data/postgre/data/*

# 从主库拉全量基础备份,-R 会生成 standby.signal 并写 primary_conninfo
su - postgres -c "pg_basebackup -h 192.168.24.21 -U repl_user -D /data/postgre/data -Xs -R -P"

# 启动
systemctl start postgresql-14

参数说明:

参数 作用
-h / -U 指定主库和复制用户
-Xs 备份期间 WAL 通过流传输(不占用主库磁盘,比 -Xf 快)
-R 自动生成 standby.signal + 写入 primary_conninfo------这步是"备库身份"的关键
-P 显示进度

检查备库数据目录:

bash 复制代码
ls /data/postgre/data/standby.signal     # 存在 = 备库身份正确
cat /data/postgre/data/postgresql.auto.conf   # 有 primary_conninfo 行

postgresql.auto.conf 说明-R 生成的 primary_conninfo 写在 auto.conf 里而不是 postgresql.conf(避免 pg_basebackup 覆盖配置文件)。以后改连接串用 ALTER SYSTEM 命令,不要手改这个文件。


6. 流复制验证

主库看复制状态

sql 复制代码
select application_name, client_addr, state, sync_state, replay_lag
from pg_stat_replication;
text 复制代码
 application_name |  client_addr  |   state   | sync_state |  replay_lag
------------------+---------------+-----------+------------+--------------
 walreceiver      | 192.168.24.22 | streaming | async      | 00:00:00.0018

state = streaming 即正常;replay_lag 毫秒级说明备库跟得很紧。

备库看恢复状态

sql 复制代码
select pg_is_in_recovery(), pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn();
text 复制代码
 t | 15/69212F20 | 15/69212F20

t 表示处于恢复模式,两个 LSN 一致 = 接收即重放,零积压。


7. 双机 WAL 归档设计

8.1 原理:standby 归档

很多教程只教主库归档,忽略了备库。备库在恢复模式下同样可以归档它收到的 WAL ------只要 archive_mode = always(注意:on 不够,备库归档必须 always)。

备库 postgresql.conf

ini 复制代码
# ===== 备库 pg-node-2 =====
wal_level = replica
archive_mode = always                        # 备库归档的关键!
archive_command = 'gzip -c %p > /data/backups/waldata/%f.gz && test -f /data/backups/waldata/%f.gz'
wal_keep_size = 1GB

8.2 为什么双机都归档

  • 主库归档:主库出故障、误删数据时,用"最近的 basebackup + 主库归档"做 PITR 恢复主库
  • 备库归档 :备库是容灾主力,它归档自己的 WAL 后,waldata 目录成为一部分"离线备份";且每日 basebackup 也是从备库做的------备份集 + WAL 在备库上自成一套
  • 两台归档目录互不相干,清理策略各管各的

8.3 备库归档验证

sql 复制代码
-- 备库上
select archived_count, failed_count, last_archived_wal, last_archived_time from pg_stat_archiver;

确认 failed_count 不再增长。


8. 自动化:每日基础备份 + 7 天清理

9.1 每日基础备份(备库,基于目录名保留 7 天)

备份在没有业务压力的备库本机执行 ------pg_basebackup 连本机(备库)自己,把备库的物理快照打包走,主库完全无感知。

bash 复制代码
cat > /data/backups/pg_base_backup.sh <<'EOF'
#!/bin/bash
# ================================================================
# 每日基础备份脚本(在备库上运行)
# 产物: /data/backups/wal-basic-backup/backup_YYYYMMDD_HHMMSS/
#       base.tar.gz(全量数据)+ pg_wal.tar.gz(备份期间 WAL)
#       backup_manifest(完整性清单)
# ================================================================
export PATH=$PATH:/usr/pgsql-14/bin    # cron 环境 PATH 常缺 PG 二进制目录,务必加

BACKUP_BASE_DIR="/data/backups/wal-basic-backup"    # 备份集根目录
RETENTION_DAYS=7                                    # 保留天数(必须 >= 能容忍的恢复窗口)
LOG_FILE="/var/log/pg_base_backup.log"              # 备份日志

# ---------- 准备工作 ----------
mkdir -p "${BACKUP_BASE_DIR}" "$(dirname "${LOG_FILE}")"
if [ $? -ne 0 ]; then
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] 无法创建备份或日志目录,退出" | tee -a "${LOG_FILE}" >&2
    exit 1
fi

# 生成带时间戳的备份目录名,如 backup_20260827_104051
BACKUP_NAME="backup_$(date +%Y%m%d_%H%M%S)"
BACKUP_DIR="${BACKUP_BASE_DIR}/${BACKUP_NAME}"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "${LOG_FILE}"
}

# ---------- 执行备份 ----------
log "开始制作基础备份: ${BACKUP_NAME}"

# 不写 -h/-U = 连接本机(备库自己)。从备库做备份是刻意设计,不是漏写!
pg_basebackup \
  -D "${BACKUP_DIR}" \
  -Ft -z -P -v \          # -Ft tar 打包,-z gzip 压缩,-P/-v 显示进度
  -X stream \             # WAL 走流传输
  -c fast >> "${LOG_FILE}" 2>&1   # fast checkpoint 减轻主库压力

if [ $? -eq 0 ]; then
    log "基础备份成功完成"

    # ---------- 历史备份清理(基于目录名日期,比 find -mtime 更可靠) ----------
    log "开始清理 ${RETENTION_DAYS} 天前的旧备份(基于目录名日期)"
    cd "${BACKUP_BASE_DIR}" || exit 1

    cutoff_date=$(date -d "-${RETENTION_DAYS} days" +%Y%m%d)    # 例如 20260820
    log "截止日期:${cutoff_date}"

    for dir in backup_*; do
        [ -d "$dir" ] || continue

        # 目录名 backup_20260827_104051 -> 提取 20260827
        dir_date=${dir#backup_}          # 去掉前缀
        dir_date=${dir_date%%_*}         # 只取第一个 _ 前的部分,即 YYYYMMDD

        # 格式校验:必须为 8 位数字,防止误删
        if [[ ! ${dir_date} =~ ^[0-9]{8}$ ]]; then
            log "警告:目录名格式异常,跳过 ${dir}"
            continue
        fi

        # YYYYMMDD 字符串直接比较大小 = 日期比较
        if [[ "${dir_date}" < "${cutoff_date}" ]]; then
            log "删除过期备份目录:${dir}"
            rm -rf "${dir}"
        fi
    done
else
    log "基础备份失败!"
    exit 1
fi

log "备份任务结束"
echo "----------------------------------------" >> "${LOG_FILE}"
EOF
chmod +x /data/backups/pg_base_backup.sh

cron(备库每天 23:00):

bash 复制代码
(crontab -l 2>/dev/null; echo "00 23 * * * /bin/bash /data/backups/pg_base_backup.sh") | crontab -

9.2 WAL 归档清理(备库)

bash 复制代码
cat > /data/backups/pg_clean_wal.sh <<'EOF'
#!/bin/bash
# ================================================================
# WAL 归档清理脚本(在备库上运行,清 /data/backups/waldata)
# 与主库的 clean_archive.sh 各管各的目录
# ================================================================
ARCHIVE_DIR="/data/backups/waldata"     # 备库归档目录
RETENTION_DAYS=7                        # 保留 7 天
LOG_FILE="/var/log/pg_wal_clean.log"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG_FILE"
}

# ---------- 前置检查 ----------
if [ ! -d "$ARCHIVE_DIR" ]; then
    log "错误:归档目录 ${ARCHIVE_DIR} 不存在,退出。"
    exit 1
fi

mkdir -p "$(dirname "$LOG_FILE")"

# ---------- 清理旧 WAL ----------
log "开始清理 ${RETENTION_DAYS} 天前的WAL归档文件"
deleted_count=$(find "$ARCHIVE_DIR" -type f -name "*.gz" -mtime +${RETENTION_DAYS} -print -delete | wc -l)
log "清理完成,共删除 ${deleted_count} 个过期WAL文件"
echo "----------------------------------------" >> "$LOG_FILE"
EOF
chmod +x /data/backups/pg_clean_wal.sh

cron(备库每天 23:30):

bash 复制代码
(crontab -l 2>/dev/null; echo "30 23 * * * /bin/bash /data/backups/pg_clean_wal.sh") | crontab -

9.3 主库归档清理(同款思路)

bash 复制代码
mkdir -p /data/postgre/scripts
cat > /data/postgre/scripts/clean_archive.sh <<'EOF'
#!/bin/bash
# 主库归档目录清理:/data/postgre/archive 保留 7 天
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> /var/log/pg_archive_clean.log; }
deleted=$(find /data/postgre/archive -name "*.gz" -mtime +7 -delete | wc -l)
log "清理完成,删除 ${deleted} 个过期归档"
EOF
chmod +x /data/postgre/scripts/clean_archive.sh
# 主库 cron 23:30(如果想与业务备份错开,改 45 23 即可)
(crontab -l 2>/dev/null; echo "30 23 * * * /bin/bash /data/postgre/scripts/clean_archive.sh") | crontab -

务必想清楚删除窗口-mtime +7 就是你的 PITR 恢复窗口。保留 7 天 = 能恢复到 7 天前的任意时刻。WAL 文件删除后,对应时间点的数据就永远找不回了。


9. 断档保护:物理复制槽

10.1 问题背景

备库断网几天,重连后主库 pg_wal 里那几天的 WAL 已经被 checkpoint 回收------备库从断点之后再也追不上了 ,只能重新 basebackup。复制槽解决这个问题:主库保证槽所需的 WAL 绝不回收

10.2 创建槽(主库)

sql 复制代码
-- 主库
select pg_create_physical_replication_slot('standby_slot');
select slot_name, slot_type, active from pg_replication_slots;
-- 预期: standby_slot | physical | f   (绑定前 f 正常)

10.3 备库绑定槽:正确姿势

物理复制槽是由备库的独立参数 primary_slot_name 指定的,不是 primary_conninfo 连接串的参数!

sql 复制代码
-- 备库
alter system set primary_slot_name = 'standby_slot';
select pg_reload_conf();

注意生效条件primary_conninfo / primary_slot_name 的修改不会让正在运行的 walreceiver 重连,必须让它重建连接:

bash 复制代码
# 备库,任选其一
systemctl restart postgresql-14        # 最干净(备库无业务,重启无感)

# 或只 kill walreceiver 进程,postmaster 会自动拉起新的
pkill -f "postgres.*walreceiver"

10.4 槽上限保险丝

槽有"无人消费"的天然风险:如果备库长期离线,主库 WAL 会一直保留直到撑爆磁盘。PG 13+ 提供上限:

sql 复制代码
-- 主库
alter system set max_slot_wal_keep_size = '20GB';
select pg_reload_conf();

超过 20GB 后主库开始警告(pg_wal 不再继续为槽保留 WAL),提醒你该去修备库了。

20GB 的精确语义(读者最容易误解的一点):

  • 保护范围 = 槽的 restart_lsn → 主库当前 LSN 之间累计的 WAL 量,不是断线时间
  • 在保护范围内(< 20GB):备库断线几天甚至几周都无所谓------重连后自动从断点续传追平,主库归档、备份一切照常,系统完全自愈,零人工干预
  • 超出保护范围(> 20GB) :超出部分的原始 WAL 被删,备库追不平,需要重新做一次 pg_basebackup;但归档、备份、PITR 依然完好

注意:超限删除的只是 pg_wal 中为槽保留的原始 WAL,归档目录里的 .gz 在 WAL 产生时就已完成写入,不受任何影响------PITR 恢复能力完好,受影响的仅是失联备库的增量追平能力。

20GB 是字节预算,不是时间预算:阈值与数据库的 WAL 生成速率有关,业务重的库和轻负载的库含义完全不同。自查当前余量:

sql 复制代码
select slot_name, active,
       pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)/1024/1024/1024 as gap_gb
from pg_replication_slots;   -- gap_gb = 当前容错余量(GB)

再观察归档目录一天的增量(du -sh /data/postgre/archive),按 20GB ÷ 每天 WAL 量 = 容错天数 换算。容错窗口不够时,按业务容忍度调大上限或收紧清理策略。

10.5 验证绑定成功(主库)

sql 复制代码
select slot_name, slot_type, active, restart_lsn from pg_replication_slots;
-- 预期: standby_slot | physical | t   ← active=t 就是绑定成功

绑定后 restart_lsn 会随备库消费推进,主库 WAL 不会净增长。


10. 坑点实录

以下每个坑都是我们从真实环境里踩出来的,按"错误表现 → 根因 → 修复"记录。

坑 1:root 属主毁天灭地 ------ 归档目录和数据目录一个都不放过

表现 1(归档失败)

复制代码
LOG:  archive command failed with exit code 1
DETAIL: The failed archive command was: gzip -c pg_wal/000000010000000000000001 > /data/postgre/archive/000000010000000000000001.gz && test -f ...
sh: /data/postgre/archive/000000010000000000000001.gz: Permission denied
WARNING: archiving write-ahead log file "000000010000000000000001" failed too many times, will try again later

表现 2(备份失败)

复制代码
pg_basebackup: error: could not get COPY data stream: ERROR: could not open file "./postgresql.conf.bak.20260714": Permission denied
pg_basebackup: removing data directory "/data/backups/wal-basic-backup/backup_20260827_104051"

根因:都是同一个病根------postgres 用户没有文件操作权限:

  • 归档目录 mkdir 时用了 root 身份(drwxr-xr-x root root)→ 归档进程无法创建 .gz → 每 60 秒重试一次,失败计数一路涨到 42 次
  • 数据目录里混进了 root 属主的文件(postgresql.conf.bak.20260714-rw------- root root)→ pg_basebackup 打包时 postgres 进程读不了它 → 整份备份作废并自动删除(原子性:不留半成品)

修复

bash 复制代码
# ① 归档目录(主/备库各自执行)
chown postgres:postgres /data/postgre/archive
chmod 700 /data/postgre/archive

# ② 数据目录:找出所有非 postgres 属主的文件
find /data/postgre/data -maxdepth 1 ! -user postgres -ls
# 处理:chown + chmod,或直接移出数据目录(推荐)
chown postgres:postgres /data/postgre/data/postgresql.conf.bak.20260714
chmod 644 /data/postgre/data/postgresql.conf.bak.20260714

要点与铁律

  • 报错可区分:目录不存在 → No such file or directory;存在但没权限 → Permission denied
  • 修复归档权限后不需要重启,PG 会自动重试归档
  • 数据目录和归档目录里严禁 root 属主的任何文件 ------.bak.log、README 等一律放目录外面(数据目录里放配置副本是很多人的习惯,恰恰是 basebackup 隐形杀手)

坑 2:primary_conninfo 改完不重启 walreceiver 不生效

primary_conninfo / primary_slot_name 是 SIGHUP 可重载参数,但 walreceiver 不会因为 reload 而断开重连------旧连接会一直用旧配置。改完后必须:

bash 复制代码
systemctl restart postgresql-14        # 或
pkill -f "postgres.*walreceiver"       # postmaster 会自动拉起新进程

坑 3:备库到底能不能做 basebackup?

能! 官方文档明确支持:pg_basebackup can make a base backup from not only the master but also the standby(前提:备库 max_wal_sendershot_standby 开启且允许 replication 连接,并建议主库 full_page_writes=on)。

从备库做备份是最优实践 :备份的 IO 压力全部落在备库,主库零占用。本文的每日备份脚本就是"备库连备库自己"(不写 -h 就是本机)。

被这个坑骗过的常见过程:看到 pg_basebackup 连上备库后 COPY 逻辑正常,反而怀疑"是不是连到主库了"------实际上从备库做备份完全合法。判断连接目标看报错:连备库 时若恢复模式限制会报 cannot start backup during recovery;能 COPY 数据就是正常的非恢复模式服务器(主库)或允许备份的备库。

坑 4:脚本漏了 cron 的 PATH

cron 的 PATH 通常只有 /usr/bin:/bin,PG 二进制目录 /usr/pgsql-14/bin 不在其中。脚本开头务必:

bash 复制代码
export PATH=$PATH:/usr/pgsql-14/bin

否则定时任务会在命令行找不到 pg_basebackup 而静默失败(手动跑正常、cron 跑失败,最迷惑人的一种)。


11. 容灾切换手册

12.1 主库故障 → 提升备库

bash 复制代码
# 1) 确认主库不可用(多路确认,别误判)
pg_isready -h 192.168.24.21

# 2) 备库提升为主库
su - postgres -c "pg_ctl promote -D /data/postgre/data"
# 或 psql: select pg_promote();

# 3) 确认提升
su - postgres -c "psql -c 'select pg_is_in_recovery();'"   # 应为 f

# 4) 应用/连接串切换到 192.168.24.22(业务侧)

切换即成功:新主库的归档目录仍是 /data/backups/waldata(配置原样保留,归档继续工作)。

12.2 旧主库修好后 → 重建为新备库

bash 复制代码
# 1) 旧主库(数据已过期)停库并清数据目录(别动归档目录!)
systemctl stop postgresql-14
rm -rf /data/postgre/data/*

# 2) 从新主库拉基础备份(-R 自动生成 standby.signal)
su - postgres -c "pg_basebackup -h 192.168.24.22 -U repl_user -D /data/postgre/data -Xs -R -P"

# 3) 启动
systemctl start postgresql-14

4) 在新主库(原备库)上重建槽(旧槽在旧主库上,已随重建作废):

sql 复制代码
select pg_create_physical_replication_slot('standby_slot');

5) 新备库绑定槽

sql 复制代码
alter system set primary_slot_name = 'standby_slot';
select pg_reload_conf();

6) 配置对齐(新备库 = 原主库,归档方向要跟着改):

bash 复制代码
# 原主库配置是 archive_mode=on + /data/postgre/archive
# 作为备库应改为(与另一台一致):
#   archive_mode = always
#   archive_command = 'gzip -c %p > /data/backups/waldata/%f.gz && test -f /data/backups/waldata/%f.gz'

7) 验证

sql 复制代码
-- 新主库
select application_name, state, replay_lag from pg_stat_replication;  -- streaming
select slot_name, active from pg_replication_slots;                   -- t

12. 附录:日常检查命令

# 检查项 命令(注意机器) 正常表现
1 流复制 主库:select application_name, client_addr, state, replay_lag from pg_stat_replication; streaming + 毫秒级
2 主库:select slot_name, active, restart_lsn from pg_replication_slots; active=t
3 主库归档 主库:select archived_count, failed_count, last_archived_time from pg_stat_archiver; failed_count 停止增长
4 备库恢复 备库:select pg_is_in_recovery(), pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn(); t + 两 LSN 一致
5 备库归档 备库:select archived_count, failed_count from pg_stat_archiver; failed_count 不增长
6 备份集 备库:ls -lt /data/backups/wal-basic-backup/ 最近目录含 base.tar.gz+pg_wal.tar.gz+backup_manifest
7 归档目录 两台:`ls -lt <归档目录> head -3`

尾声

这套方案是一条完整的数据保护链:

复制代码
主库实时写入 → 流复制到备库(ms 级) → 主/备双机 gzip 归档(7 天)
           → 备库每日 basebackup(7 天) → 任意时间点可恢复

几个最终提醒

  1. 恢复演练迟早要做的------备份只做了不练,等于没有。每月挑一天从备份集 + WAL 恢复到测试实例验证一下。
  2. 清理窗口 = 恢复窗口 ,改 RETENTION_DAYS 前先问自己:业务能接受丢多少天的数据?
  3. 本文的 password 请全部替换为你的真实密码;.pgpassALTER SYSTEM 存密码后,配置文件的权限必须 600
相关推荐
树下水月11 分钟前
python3 使用canal监听后,kafka数据同步从mysql到clickhouse数据库 完整复盘
数据库·mysql·kafka
深念Y11 分钟前
QQ 数据库解密与聊天记录导出 — 技术文档
数据库·sqlite·手机·数据恢复·逆向·二进制·qq
程序员-Benothing13 分钟前
什么是批量数据入库?相比单条插入有什么优势?
数据库
我滴老baby18 分钟前
部署 Portainer CE,把日志、镜像和数据卷搬进网页
数据库·人工智能·架构
kyle~25 分钟前
工业机械臂 --- 抱闸系统(Holding Brake)
机器人·自动化·硬件
qq_4260039628 分钟前
多语言新增语种全量测试策略
前端·javascript·python·pycharm·自动化
吠品34 分钟前
TortoiseSVN 状态图标不显示的排查与解决办法
java·服务器·数据库
ltl38 分钟前
PostgreSQL WAL 内部机制:XLogInsert 到 Checkpoint
数据库
cetcht888839 分钟前
深耕配电智能化升级 多场景项目落地筑牢电力运维安全防线
大数据·数据库·人工智能