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.signal和postgresql.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)
可能原因:
standby.signal文件不存在primary_conninfo配置错误- 主服务器未允许从服务器连接
- 网络连接问题
解决方案:
- 检查 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
- 检查 primary_conninfo:
bash
cat /disk/data/postgres/pgdata/postgresql.auto.conf | grep primary_conninfo
# 应该包含正确的主服务器IP、端口、用户名和密码
- 检查主服务器连接:
bash
# 在从服务器容器内测试
docker exec postgresql-16 pg_isready -h 192.168.1.9 -p 5432 -U replication_user
- 检查主服务器 pg_hba.conf:
bash
# 在主服务器上
docker exec postgresql-16 cat /var/lib/postgresql/data/pgdata/pg_hba.conf | grep replication
# 应该包含从服务器IP的规则
- 使用修复脚本:
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)
可能原因:
- 主服务器
pg_hba.conf未允许从服务器连接 - 主服务器
wal_level或max_wal_senders配置不正确 - 从服务器未正确配置
解决方案:
- 检查主服务器配置:
bash
sudo bash pg16_check_master_for_slave.sh
- 检查主服务器 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
- 检查主服务器 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
可能原因:
- 主服务器未运行
- 主服务器未监听正确地址
- 防火墙阻止连接
- 网络问题
解决方案:
- 检查主服务器是否运行:
bash
docker ps | grep postgresql-16
- 检查主服务器监听地址:
bash
docker exec postgresql-16 psql -U postgres -c "SHOW listen_addresses;"
# 应该是 * 或包含主服务器IP
- 测试网络连接:
bash
# 在从服务器上
ping 192.168.1.9
telnet 192.168.1.9 5432
5.6 数据未同步
症状:
在主服务器上创建数据库和表后,从服务器上看不到。
可能原因:
- 流复制未正确启动
- 同步延迟
- 从服务器在
pg_basebackup之前就存在数据
解决方案:
- 检查流复制状态:
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;"
- 检查 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();"
- 重新创建从服务器:
如果数据是在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 容器主从流复制的关键点:
- 主服务器配置 :
wal_level = replica、max_wal_senders >= 1、pg_hba.conf允许从服务器连接 - 从服务器配置 :使用
pg_basebackup创建数据目录,确保目录为空,系统标识符匹配 - 参数一致性:从服务器参数必须 >= 主服务器
- 路径正确性 :
pg_basebackup使用容器内路径,挂载路径必须一致 - 权限设置 :数据目录所有者必须是
999:999
遇到问题时,使用诊断脚本定位问题,然后使用相应的修复脚本解决。
文档版本 : 1.0
最后更新 : 2026-02-04
作者: AI Assistant