【PostgreSQL】如何实现主从复制?
PostgreSQL 主从复制最简单、最常用、官方推荐的方法是流复制 (Streaming Replication)。它利用 PostgreSQL 的预写日志 (WAL, Write-Ahead Log) 进行数据同步。
这里介绍一个基于 pg_basebackup
和 standby.signal
的简单配置方法,适用于 PostgreSQL 12 及更高版本 (因为 standby.signal
和将 primary_conninfo
直接写入 postgresql.auto.conf
是其特点)。
核心思想:
-
Primary (主库) 配置: 开启 WAL 日志发送功能,并允许副本连接。
-
Replica (从库) 初始化: 使用
pg_basebackup
从主库获取一份基准备份,并自动配置为从库。 -
Replica (从库) 启动: 启动从库服务,它会自动连接主库并开始接收 WAL 日志。
环境准备:
假设你有两台服务器(或虚拟机):
-
Primary (主库):
192.168.1.101
-
Replica (从库):
192.168.1.102
两台机器上都已安装 PostgreSQL,并且能够相互 ping 通。
第一步:Primary (主库) 配置
- 编辑
postgresql.conf
文件
找到你的 PostgreSQL 数据目录下的 postgresql.conf
文件 (通常在 /etc/postgresql/1X/main/postgresql.conf
或 $PGDATA/postgresql.conf
)。修改或添加以下参数:
# 允许所有IP连接(或者指定从库IP)listen_addresses = '*'# 开启WAL日志级别,`replica` 足够用于流复制wal_level = replica# 最大同时发送WAL日志的进程数(根据需要设置,一般5-10个足够)max_wal_senders = 10# 保持WAL日志文件大小,防止从库断开重连后找不到WAL日志# 生产环境中建议设置大一些,如 1GB 或更多,具体取决于WAL生成速度和网络稳定性wal_keep_size = 512MB # PostgreSQL 13及以前是 wal_keep_segments# 如果是 PostgreSQL 13 以前的版本,可能需要设置 wal_keep_segments = 32 (每个16MB,总共512MB)# 可选:开启WAL归档。对于生产环境非常重要,用于PITR (Point-In-Time Recovery) 和从库长时间掉线后的恢复。# 如果只是简单的流复制测试,可以暂时不开启,但强烈建议开启。# archive_mode = on# archive_command = 'cp %p /mnt/pg_archive/%f' # 替换为你的归档目录
- 编辑
pg_hba.conf
文件
允许从库以复制用户身份连接。找到你的 pg_hba.conf
文件 (与 postgresql.conf
在同一目录下),添加以下一行:
# TYPE DATABASE USER ADDRESS METHODhost replication repuser 192.168.1.102/32 md5# 或者允许所有IP进行复制连接,但不推荐用于生产环境# host replication repuser 0.0.0.0/0 md5
-
replication
是一个特殊的“数据库”,表示复制连接。 -
repuser
是你将要创建的复制用户。 -
192.168.1.102/32
是你的从库IP地址。
- 创建复制用户
以 postgres
用户身份连接到主库,创建用于复制的用户:
# 切换到postgres用户sudo -i -u postgres# 进入psql控制台psql# 创建复制用户CREATE USER repuser REPLICATION LOGIN CONNECTION LIMIT -1 ENCRYPTED PASSWORD 'your_secure_password';# 退出psql\q
注意: 请将 your_secure_password
替换为强密码。
- 重启 Primary (主库) 服务
使配置生效:
sudo systemctl restart postgresql# 或者根据你的系统和PostgreSQL版本使用相应的命令,例如:# sudo /etc/init.d/postgresql restart
第二步:Replica (从库) 初始化
- 停止 Replica (从库) 服务 (如果正在运行)
如果你的从库上已经有 PostgreSQL 服务在运行,请先停止它。
sudo systemctl stop postgresql
- 清空从库数据目录 (重要!)
警告: 这会删除从库上所有现有的 PostgreSQL 数据。请确保你清楚自己在做什么!
找到从库的 PostgreSQL 数据目录 (例如 /var/lib/postgresql/1X/main
或 /usr/local/pgsql/data
)。
# 假设数据目录是 /var/lib/postgresql/14/mainsudo rm -rf /var/lib/postgresql/14/main/*
- 使用
pg_basebackup
获取基准备份
切换到能够访问到 PostgreSQL 数据目录的用户 (通常是 postgres
用户),然后执行以下命令:
sudo -i -u postgres# 替换 primary_ip, repuser, your_secure_password, 和你的数据目录路径# 例如:pg_basebackup -h 192.168.1.101 -U repuser -D /var/lib/postgresql/14/main -F p -X stream -P -v -R -wpg_basebackup -h 192.168.1.101 \-U repuser \-D /var/lib/postgresql/14/main \-F p \-X stream \-P \-v \-R \-w
参数说明:
-
-h 192.168.1.101
: 主库的IP地址。 -
-U repuser
: 用于连接主库的复制用户。 -
-D /var/lib/postgresql/14/main
: 从库的数据目录。 -
-F p
: 输出格式为 “plain” (纯文件),而不是 “tar”。 -
-X stream
: 在备份过程中同时流式传输 WAL 日志。 -
-P
: 显示进度。 -
-v
: 详细输出。 -
-R
: 最重要! 自动创建standby.signal
文件并在postgresql.auto.conf
中添加primary_conninfo
。这是简化配置的关键! -
-w
: 提示输入密码(如果密码不在环境变量中)。
执行命令后会要求输入 repuser
的密码。
- 确认从库配置
pg_basebackup -R
已经帮你完成了大部分配置。你可以检查从库数据目录下是否生成了:
-
standby.signal
文件(这是一个空文件,PostgreSQL 启动时检测到它就会进入恢复模式)。 -
postgresql.auto.conf
文件中包含了primary_conninfo
参数,类似这样:
# Do not edit this file manually!# It will be overwritten by the ALTER SYSTEM command.primary_conninfo = 'user=repuser password=your_secure_password host=192.168.1.101 port=5432 sslmode=prefer sslcompression=0 gsslib=0 channel_binding=prefer'
pg_basebackup
会自动将密码写入这个文件。在生产环境中,出于安全考虑,你可能需要手动修改 primary_conninfo
,将密码从明文移除,并通过 .pgpass
文件或其他更安全的方式提供密码。但对于简单测试,这样是可以的。
第三步:启动 Replica (从库) 服务并验证
- 启动 Replica (从库) 服务
sudo systemctl start postgresql
- 验证复制状态
- 在 Primary (主库) 上检查:
sudo -i -u postgrespsql -c "SELECT client_addr, state, sync_state, sync_priority FROM pg_stat_replication;"
你应该看到一行输出,其中 client_addr
是从库的IP,state
是 streaming
,sync_state
通常是 async
(异步)。
- 在 Replica (从库) 上检查:
sudo -i -u postgrespsql -c "SELECT pg_is_in_recovery();"
应该返回 t
(true)。
psql -c "SELECT pg_last_wal_replay_lsn();"# 记录这个LSN# 在主库上运行以下命令,比较LSN,看是否接近或相同psql -h 192.168.1.101 -U postgres -c "SELECT pg_current_wal_lsn();"
pg_last_wal_replay_lsn()
(从库) 应该接近或等于 pg_current_wal_lsn()
(主库)。
在主库上插入一些数据,然后在从库上查询,看看数据是否同步过来。
主库:
CREATE TABLE test_repl (id INT, name TEXT);INSERT INTO test_repl VALUES (1, 'hello');SELECT * FROM test_repl;
从库:
SELECT * FROM test_repl; -- 应该能看到 'hello'-- 尝试插入数据会失败,因为从库是只读的-- INSERT INTO test_repl VALUES (2, 'world');
重要注意事项:
-
防火墙: 确保主库的 5432 端口对从库开放,反之亦然(如果需要从从库连接到主库)。
-
WAL Archiving (WAL 归档): 虽然不是流复制的硬性要求,但在生产环境中强烈建议开启。它能让你在从库长时间掉线后通过归档日志进行恢复,也方便进行 Point-in-Time Recovery (PITR)。
-
安全:
-
repuser
应该使用强密码。 -
在
pg_hba.conf
中尽可能指定从库的精确 IP 地址,而不是0.0.0.0/0
。 -
primary_conninfo
中的密码最好通过.pgpass
文件或其他方式保护,而不是明文存储在postgresql.auto.conf
中。 -
同步 vs. 异步: 上述配置是异步复制。这意味着主库在写入数据后不需要等待从库确认就已经提交事务。这提供了更好的性能,但可能会在主库崩溃时导致少量数据丢失。同步复制可以防止数据丢失,但会增加主库的事务延迟。
-
高可用性: 这个配置只是实现了主从复制,不包含自动故障转移。如果主库挂了,你需要手动提升从库为主库,并配置其他从库连接到新的主库。这通常需要使用像 Patroni、PgBouncer、repmgr 或 corosync/pacemaker 等工具来构建高可用集群。
-
版本兼容性: 本指南主要针对 PostgreSQL 12 及更高版本。对于 PostgreSQL 11 及更早版本,
recovery.conf
文件扮演了standby.signal
和primary_conninfo
的角色。
这个方法是搭建 PostgreSQL 流复制最简单、最快捷的方式,非常适合初次尝试或测试环境。