PostgreSQL 16 容器主从流复制

PostgreSQL 16 容器主从流复制总结

一、概述

本文档总结了在 Docker 容器环境中配置 PostgreSQL 16 主从流复制(Streaming Replication)的完整流程、常见问题及解决方案。

环境要求

  • 两台 Ubuntu 服务器(或 Linux Mint)
  • 每台服务器运行 PostgreSQL 16 Docker 容器
  • 两台服务器之间网络互通
  • 主服务器 IP:192.168.1.9(示例)
  • 从服务器 IP:192.168.1.100(示例)

二、主服务器配置

2.1 创建复制用户

bash 复制代码
# 进入主服务器容器
docker exec -it postgresql-16 psql -U postgres

# 创建复制用户
CREATE USER replication_user WITH REPLICATION PASSWORD 'passw0rd';

# 退出
\q

2.2 配置 postgresql.conf

bash 复制代码
# 在容器内编辑配置文件
docker exec -it postgresql-16 bash
vi /var/lib/postgresql/data/pgdata/postgresql.conf

添加或修改以下配置:

conf 复制代码
# 启用 WAL 归档(必须)
wal_level = replica

# 设置最大 WAL 发送进程数(至少为 1)
max_wal_senders = 10

# 设置最大复制槽数(可选,用于逻辑复制)
max_replication_slots = 10

# 启用热备份(允许从服务器执行只读查询)
hot_standby = on

或者使用 ALTER SYSTEM 命令:

bash 复制代码
docker exec postgresql-16 psql -U postgres -c "ALTER SYSTEM SET wal_level = 'replica';"
docker exec postgresql-16 psql -U postgres -c "ALTER SYSTEM SET max_wal_senders = 10;"
docker exec postgresql-16 psql -U postgres -c "ALTER SYSTEM SET hot_standby = 'on';"

2.3 配置 pg_hba.conf

bash 复制代码
# 在容器内编辑
docker exec -it postgresql-16 bash
vi /var/lib/postgresql/data/pgdata/pg_hba.conf

在文件末尾添加(替换为从服务器实际 IP):

复制代码
# 允许从服务器连接进行流复制
host    replication     replication_user     192.168.1.100/32         md5

或者直接在主服务器上执行:

bash 复制代码
# 查看当前配置
docker exec postgresql-16 cat /var/lib/postgresql/data/pgdata/pg_hba.conf | grep replication

# 如果不存在,需要手动添加
docker exec -it postgresql-16 bash
echo "host replication replication_user 192.168.1.100/32 md5" >> /var/lib/postgresql/data/pgdata/pg_hba.conf

2.4 重启主服务器容器

bash 复制代码
docker restart postgresql-16

2.5 验证主服务器配置

bash 复制代码
# 检查 wal_level
docker exec postgresql-16 psql -U postgres -c "SHOW wal_level;"
# 应该返回: replica

# 检查 max_wal_senders
docker exec postgresql-16 psql -U postgres -c "SHOW max_wal_senders;"
# 应该返回: 10(或更大)

# 检查监听地址
docker exec postgresql-16 psql -U postgres -c "SHOW listen_addresses;"
# 应该返回: * 或包含主服务器IP

三、从服务器配置

3.1 停止从服务器容器

bash 复制代码
docker stop postgresql-16

3.2 备份并清理数据目录

bash 复制代码
# 备份现有数据(如果重要)
mv /disk/data/postgres/pgdata /disk/data/postgres/pgdata_backup_$(date +%Y%m%d_%H%M%S)

# 创建新的空数据目录
mkdir -p /disk/data/postgres/pgdata
chown -R 999:999 /disk/data/postgres/pgdata

# 验证目录为空
ls -la /disk/data/postgres/pgdata
# 应该只显示 . 和 .. 目录

3.3 执行 pg_basebackup

使用自动脚本(推荐):

bash 复制代码
sudo bash pg16_recreate_slave_from_master.sh

或手动执行:

bash 复制代码
docker run --rm \
  -v /disk/data/postgres:/var/lib/postgresql/data \
  -e PGPASSWORD=passw0rd \
  postgres:16 \
  pg_basebackup \
    -h 192.168.1.9 \
    -p 5432 \
    -U replication_user \
    -D /var/lib/postgresql/data/pgdata \
    -F p \
    -X stream \
    -P \
    -R \
    -v

参数说明:

  • -h: 主服务器 IP 地址
  • -p: 主服务器端口(默认 5432)
  • -U: 复制用户名
  • -D: 目标数据目录(容器内的路径)
  • -F p: 使用纯文本格式(plain format)
  • -X stream: 启用流复制,在备份过程中流式传输 WAL
  • -P: 显示进度
  • -R: 自动创建 standby.signalpostgresql.auto.conf 文件
  • -v: 详细输出

3.4 验证备份结果

bash 复制代码
# 检查关键文件
ls -la /disk/data/postgres/pgdata/

# 应该包含:
# - PG_VERSION
# - standby.signal
# - postgresql.auto.conf
# - base/ 目录
# - global/ 目录

# 检查 primary_conninfo
cat /disk/data/postgres/pgdata/postgresql.auto.conf | grep primary_conninfo

# 检查 standby.signal
ls -la /disk/data/postgres/pgdata/standby.signal

3.5 设置权限

bash 复制代码
chown -R 999:999 /disk/data/postgres/pgdata

3.6 启动从服务器容器

bash 复制代码
docker start postgresql-16

# 等待启动
sleep 5

# 检查状态
docker exec postgresql-16 pg_isready -U postgres

四、验证流复制状态

4.1 在主服务器上检查

bash 复制代码
# 查看复制连接
docker exec postgresql-16 psql -U postgres -c "SELECT * FROM pg_stat_replication;"

# 应该显示从服务器的连接信息,包括:
# - pid: 进程ID
# - application_name: 应用名称
# - client_addr: 从服务器IP
# - state: 状态(通常是 streaming)
# - sync_state: 同步状态
# - sent_lsn, write_lsn, flush_lsn, replay_lsn: WAL 位置

4.2 在从服务器上检查

bash 复制代码
# 检查恢复模式
docker exec postgresql-16 psql -U postgres -c "SELECT pg_is_in_recovery();"
# 应该返回: t (true)

# 检查 WAL 接收器
docker exec postgresql-16 psql -U postgres -c "SELECT * FROM pg_stat_wal_receiver;"

# 应该显示:
# - pid: WAL 接收器进程ID
# - status: 状态(通常是 streaming)
# - receive_start_lsn: 开始接收的 WAL 位置
# - latest_end_lsn: 最新接收的 WAL 位置
# - sender_host: 主服务器IP
# - sender_port: 主服务器端口

# 检查 WAL 位置
docker exec postgresql-16 psql -U postgres -c "
SELECT 
    pg_last_wal_receive_lsn() AS receive_lsn,
    pg_last_wal_replay_lsn() AS replay_lsn,
    pg_last_wal_receive_lsn() - pg_last_wal_replay_lsn() AS lag_bytes;
"

4.3 测试数据同步

bash 复制代码
# 在主服务器上创建测试数据库
docker exec postgresql-16 psql -U postgres -c "CREATE DATABASE replication_test;"

# 在主服务器上创建表并插入数据
docker exec postgresql-16 psql -U postgres -d replication_test -c "
CREATE TABLE test_table (id SERIAL PRIMARY KEY, name VARCHAR(100));
INSERT INTO test_table (name) VALUES ('test1'), ('test2');
"

# 等待几秒钟让数据同步
sleep 3

# 在从服务器上查询(应该能看到数据)
docker exec postgresql-16 psql -U postgres -d replication_test -c "SELECT * FROM test_table;"

五、常见问题及解决方案

5.1 FATAL: database system identifier differs

错误信息:

复制代码
FATAL: database system identifier differs between the primary and standby
DETAIL: The primary's identifier is 7602832328514842668, the standby's identifier is 7602844093384110118.

原因:

从服务器的数据目录不是从主服务器的正确备份创建的。可能原因:

  • 从服务器数据目录在 pg_basebackup 之前不是空的
  • pg_basebackup 执行失败但没有被注意到
  • 使用了错误的备份源

解决方案:

使用自动修复脚本:

bash 复制代码
sudo bash pg16_recreate_slave_from_master.sh

或手动修复:

bash 复制代码
# 1. 停止从服务器容器
docker stop postgresql-16

# 2. 备份并删除旧数据目录
mv /disk/data/postgres/pgdata /disk/data/postgres/pgdata_backup_$(date +%Y%m%d_%H%M%S)
mkdir -p /disk/data/postgres/pgdata
chown -R 999:999 /disk/data/postgres/pgdata

# 3. 验证目录为空
ls -la /disk/data/postgres/pgdata
# 应该只显示 . 和 ..

# 4. 重新执行 pg_basebackup
docker run --rm \
  -v /disk/data/postgres:/var/lib/postgresql/data \
  -e PGPASSWORD=was70ptf \
  postgres:16 \
  pg_basebackup \
    -h 192.168.1.9 \
    -p 5432 \
    -U replication_user \
    -D /var/lib/postgresql/data/pgdata \
    -F p \
    -X stream \
    -P \
    -R \
    -v

# 5. 设置权限
chown -R 999:999 /disk/data/postgres/pgdata

# 6. 启动容器
docker start postgresql-16

5.2 FATAL: recovery aborted because of insufficient parameter settings

错误信息:

复制代码
FATAL: recovery aborted because of insufficient parameter settings
DETAIL: max_connections = 100 is a lower setting than on the primary server, where its value was 200.

原因:

从服务器的某些参数值小于主服务器,导致恢复失败。

解决方案:

在从服务器的 postgresql.auto.conf 中设置相同或更大的值:

bash 复制代码
# 检查主服务器的 max_connections
docker exec postgresql-16 psql -U postgres -c "SHOW max_connections;"

# 在从服务器上设置(在主服务器上执行,替换为从服务器IP)
docker exec postgresql-16 bash -c "echo 'max_connections = 200' >> /var/lib/postgresql/data/pgdata/postgresql.auto.conf"

# 或者直接在主机上编辑
echo "max_connections = 200" >> /disk/data/postgres/pgdata/postgresql.auto.conf
chown 999:999 /disk/data/postgres/pgdata/postgresql.auto.conf

# 重启从服务器容器
docker restart postgresql-16

注意: pg16_recreate_slave_from_master.sh 脚本会自动处理这个问题。

5.3 WAL 接收器未运行

症状:

bash 复制代码
docker exec postgresql-16 psql -U postgres -c "SELECT * FROM pg_stat_wal_receiver;"
# 返回: (0 rows)

可能原因:

  1. standby.signal 文件不存在
  2. primary_conninfo 配置错误
  3. 主服务器未允许从服务器连接
  4. 网络连接问题

解决方案:

  1. 检查 standby.signal:
bash 复制代码
ls -la /disk/data/postgres/pgdata/standby.signal
# 如果不存在,创建它
touch /disk/data/postgres/pgdata/standby.signal
chown 999:999 /disk/data/postgres/pgdata/standby.signal
  1. 检查 primary_conninfo:
bash 复制代码
cat /disk/data/postgres/pgdata/postgresql.auto.conf | grep primary_conninfo
# 应该包含正确的主服务器IP、端口、用户名和密码
  1. 检查主服务器连接:
bash 复制代码
# 在从服务器容器内测试
docker exec postgresql-16 pg_isready -h 192.168.1.9 -p 5432 -U replication_user
  1. 检查主服务器 pg_hba.conf:
bash 复制代码
# 在主服务器上
docker exec postgresql-16 cat /var/lib/postgresql/data/pgdata/pg_hba.conf | grep replication
# 应该包含从服务器IP的规则
  1. 使用修复脚本:
bash 复制代码
sudo bash pg16_fix_wal_receiver.sh

5.4 主服务器没有看到从服务器连接

症状:

bash 复制代码
# 在主服务器上
docker exec postgresql-16 psql -U postgres -c "SELECT * FROM pg_stat_replication;"
# 返回: (0 rows)

可能原因:

  1. 主服务器 pg_hba.conf 未允许从服务器连接
  2. 主服务器 wal_levelmax_wal_senders 配置不正确
  3. 从服务器未正确配置

解决方案:

  1. 检查主服务器配置:
bash 复制代码
sudo bash pg16_check_master_for_slave.sh
  1. 检查主服务器 pg_hba.conf:
bash 复制代码
docker exec postgresql-16 cat /var/lib/postgresql/data/pgdata/pg_hba.conf | grep replication
# 确保包含: host replication replication_user <从服务器IP>/32 md5
  1. 检查主服务器 WAL 配置:
bash 复制代码
docker exec postgresql-16 psql -U postgres -c "SHOW wal_level;"
docker exec postgresql-16 psql -U postgres -c "SHOW max_wal_senders;"

5.5 从服务器无法连接到主服务器

错误信息:

复制代码
psql: error: connection to server at "192.168.1.9", port 5432 failed: Connection refused

可能原因:

  1. 主服务器未运行
  2. 主服务器未监听正确地址
  3. 防火墙阻止连接
  4. 网络问题

解决方案:

  1. 检查主服务器是否运行:
bash 复制代码
docker ps | grep postgresql-16
  1. 检查主服务器监听地址:
bash 复制代码
docker exec postgresql-16 psql -U postgres -c "SHOW listen_addresses;"
# 应该是 * 或包含主服务器IP
  1. 测试网络连接:
bash 复制代码
# 在从服务器上
ping 192.168.1.9
telnet 192.168.1.9 5432

5.6 数据未同步

症状:

在主服务器上创建数据库和表后,从服务器上看不到。

可能原因:

  1. 流复制未正确启动
  2. 同步延迟
  3. 从服务器在 pg_basebackup 之前就存在数据

解决方案:

  1. 检查流复制状态:
bash 复制代码
# 在主服务器上
docker exec postgresql-16 psql -U postgres -c "SELECT * FROM pg_stat_replication;"

# 在从服务器上
docker exec postgresql-16 psql -U postgres -c "SELECT * FROM pg_stat_wal_receiver;"
  1. 检查 WAL 位置:
bash 复制代码
# 主服务器
docker exec postgresql-16 psql -U postgres -c "SELECT pg_current_wal_lsn();"

# 从服务器
docker exec postgresql-16 psql -U postgres -c "SELECT pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn();"
  1. 重新创建从服务器:
    如果数据是在 pg_basebackup 之前创建的,需要重新创建从服务器:
bash 复制代码
sudo bash pg16_recreate_slave_from_master.sh

六、重要注意事项

6.1 数据目录路径

关键点:

  • 容器挂载路径:/disk/data/postgres -> /var/lib/postgresql/data
  • 数据实际位置:/disk/data/postgres/pgdata(容器内为 /var/lib/postgresql/data/pgdata
  • pg_basebackup-D 参数必须使用容器内的路径:/var/lib/postgresql/data/pgdata

错误示例:

bash 复制代码
# ❌ 错误:使用主机路径
pg_basebackup -D /disk/data/postgres/pgdata ...

# ✅ 正确:使用容器内路径
docker run --rm -v /disk/data/postgres:/var/lib/postgresql/data ...
pg_basebackup -D /var/lib/postgresql/data/pgdata ...

6.2 系统标识符必须匹配

  • 从服务器的数据目录必须 是从主服务器的 pg_basebackup 创建的
  • 不能使用其他来源的数据目录
  • 如果系统标识符不匹配,流复制将无法启动

6.3 参数一致性

  • 从服务器的某些参数(如 max_connections)必须 >= 主服务器的值
  • 建议在 postgresql.auto.conf 中设置这些参数
  • 使用 pg16_recreate_slave_from_master.sh 脚本会自动处理

6.4 数据目录必须为空

  • 执行 pg_basebackup 之前,目标目录必须为空
  • 如果目录不为空,pg_basebackup 会失败或产生不一致的数据

6.5 网络连接

  • 确保主从服务器之间网络互通
  • 确保主服务器 pg_hba.conf 允许从服务器连接
  • 确保主服务器监听正确的地址(listen_addresses = *

6.6 权限设置

  • 数据目录的所有者必须是 999:999(PostgreSQL 容器用户)
  • 使用 chown -R 999:999 /disk/data/postgres/pgdata 设置权限

6.7 备份时机

  • 流复制只复制 pg_basebackup 之后的变更
  • pg_basebackup 之前创建的数据库和表不会自动复制
  • 如果需要这些数据,需要在主服务器上重新创建

6.8 容器重启

  • 主服务器容器重启后,从服务器会自动重连
  • 从服务器容器重启后,会继续从上次的 WAL 位置恢复
  • 如果主服务器数据目录被替换,需要重新执行 pg_basebackup

七、相关脚本说明

7.1 诊断脚本

  • pg16_diagnose_sync.sh: 诊断流复制同步问题
  • pg16_check_master_for_slave.sh: 检查主服务器配置
  • pg16_check_replication.sh: 检查从服务器复制状态

7.2 修复脚本

  • pg16_recreate_slave_from_master.sh: 重新从主服务器创建从服务器(修复系统标识符不匹配)
  • pg16_fix_wal_receiver.sh: 修复 WAL 接收器未启动问题
  • pg16_fix_replication.sh: 修复常见复制问题

7.3 设置脚本

  • pg16_setup_slave.sh: 从服务器初始设置
  • pg16_quick_slave_setup.sh: 快速设置从服务器(简化版)

7.4 测试脚本

  • pg16_test_replication.sh: 测试流复制功能
  • pg16_setup_replication_test.sh: 创建测试数据库和表

八、维护建议

8.1 定期检查

bash 复制代码
# 每天检查一次流复制状态
docker exec postgresql-16 psql -U postgres -c "SELECT * FROM pg_stat_replication;"
docker exec postgresql-16 psql -U postgres -c "SELECT * FROM pg_stat_wal_receiver;"

8.2 监控 WAL 延迟

bash 复制代码
# 检查 WAL 延迟
docker exec postgresql-16 psql -U postgres -c "
SELECT 
    pg_last_wal_receive_lsn() AS receive_lsn,
    pg_last_wal_replay_lsn() AS replay_lsn,
    pg_last_wal_receive_lsn() - pg_last_wal_replay_lsn() AS lag_bytes;
"

8.3 备份策略

  • 主服务器:定期执行 pg_dump 或使用 WAL 归档
  • 从服务器:可以作为备份源,但建议使用专门的备份工具

8.4 故障切换

如果需要将从服务器提升为主服务器:

bash 复制代码
# 在从服务器上
docker exec postgresql-16 psql -U postgres -c "SELECT pg_promote();"

# 删除 standby.signal
rm /disk/data/postgres/pgdata/standby.signal

# 重启容器
docker restart postgresql-16

九、总结

PostgreSQL 16 容器主从流复制的关键点:

  1. 主服务器配置wal_level = replicamax_wal_senders >= 1pg_hba.conf 允许从服务器连接
  2. 从服务器配置 :使用 pg_basebackup 创建数据目录,确保目录为空,系统标识符匹配
  3. 参数一致性:从服务器参数必须 >= 主服务器
  4. 路径正确性pg_basebackup 使用容器内路径,挂载路径必须一致
  5. 权限设置 :数据目录所有者必须是 999:999

遇到问题时,使用诊断脚本定位问题,然后使用相应的修复脚本解决。


文档版本 : 1.0
最后更新 : 2026-02-04
作者: AI Assistant

相关推荐
Test-Sunny2 小时前
【futu测试案例】性能测试中常见的问题汇总
数据库
zt1985q2 小时前
本地部署静态网站生成工具 Vuepress 并实现外部访问
运维·服务器·网络·数据库·网络协议
瀚高PG实验室2 小时前
数据库日志过大
数据库·瀚高数据库
2401_857683542 小时前
使用Kivy开发跨平台的移动应用
jvm·数据库·python
yangSnowy2 小时前
MySQL 分布式锁实现方案
数据库·分布式·mysql
倔强的石头1062 小时前
关系数据库替换用金仓:从 Oracle 到 KingbaseES 的迁移实战
数据库·oracle·kingbase
Leo.yuan2 小时前
制造业五大模式解析:OEM、ODM、OBM、JDM、CMT
大数据·数据库·信息可视化
铬仁2 小时前
kettle 9.2 连接达梦DM Database Server 64 V8
数据库·etl
松涛和鸣2 小时前
69、Linux字符设备驱动实战
linux·服务器·网络·arm开发·数据库·驱动开发