1. pg_rewind工具概述与适用场景
pg_rewind是PostgreSQL数据库生态中一个极具实用价值的灾难恢复工具,它能够在两个同源PostgreSQL集群的时间线(timeline)发生分叉后,高效地将落后集群同步到最新状态。这个工具特别适合处理主从切换后的旧主库重新加入集群的场景。
想象这样一个典型场景:你的PostgreSQL主库突然宕机,触发自动故障转移(failover),其中一个从库被提升为新主库。当旧主库恢复后,它已经落后于新主库,并且两者处于不同的时间线上。传统做法可能需要从新主库做一个完整的基准备份(base backup)来重建旧主库,这对于TB级数据库将耗费数小时。而pg_rewind的聪明之处在于它只复制那些发生变化的数据库块,通常能在几分钟内完成同步。
这个工具从PostgreSQL 9.5版本开始引入,到12版本已经相当成熟。它通过以下机制实现高效同步:
- 分析目标集群的WAL日志,识别自时间线分叉点以来所有被修改的数据块
- 仅从源集群复制这些变化的块到目标集群
- 完整复制所有新文件(如表文件、WAL段、配置文件等)
- 设置恢复起点,确保目标集群重启后能正确应用剩余的WAL
重要提示:使用pg_rewind前必须确保目标集群已完全关闭,且源集群要么完全关闭(使用--source-pgdata),要么正常运行(使用--source-server)。半开半闭的状态会导致灾难性后果。
2. 环境准备与前置条件检查
在PostgreSQL 12上使用pg_rewind前,有几个关键配置必须检查,否则工具将无法正常工作。这些不是可选项,而是硬性要求:
2.1 必须启用的服务器参数
在postgresql.conf中,以下参数必须正确设置:
code复制wal_log_hints = on # 或初始化集群时启用了数据校验和
full_page_writes = on # 默认即为on,但必须确认
wal_log_hints的开启使得PostgreSQL在检查点后将整个页面写入WAL,即使页面没有明显变化。这为pg_rewind提供了确定页面是否被修改的依据。如果不想启用wal_log_hints,替代方案是在初始化集群时使用initdb -k启用数据校验和。
2.2 权限与连接配置
如果计划使用在线模式(--source-server),需要确保连接用户有足够权限。虽然超级用户肯定可以,但更安全的做法是创建一个专用用户并授予最小必要权限:
sql复制CREATE USER rewind_user WITH PASSWORD 'secure_password';
GRANT EXECUTE ON FUNCTION pg_catalog.pg_ls_dir(text, boolean, boolean) TO rewind_user;
GRANT EXECUTE ON FUNCTION pg_catalog.pg_stat_file(text, boolean) TO rewind_user;
GRANT EXECUTE ON FUNCTION pg_catalog.pg_read_binary_file(text) TO rewind_user;
GRANT EXECUTE ON FUNCTION pg_catalog.pg_read_binary_file(text, bigint, bigint, boolean) TO rewind_user;
2.3 WAL归档配置验证
pg_rewind需要能够访问从分叉点到当前的所有WAL日志。验证你的WAL归档配置是否正常工作:
code复制archive_mode = on
archive_command = 'test ! -f /path/to/wal_archive/%f && cp %p /path/to/wal_archive/%f'
执行以下命令检查WAL归档是否完整:
bash复制psql -c "SELECT pg_switch_wal();"
ls -l /path/to/wal_archive/
3. pg_rewind实战操作指南
3.1 基本命令结构与参数解析
pg_rewind有两种基本工作模式,对应不同的参数组合:
模式一:源集群离线
bash复制pg_rewind \
--target-pgdata=/var/lib/postgresql/12/main \
--source-pgdata=/var/lib/postgresql/12/main_old \
--progress
模式二:源集群在线
bash复制pg_rewind \
--target-pgdata=/var/lib/postgresql/12/main \
--source-server="host=192.168.1.100 user=rewind_user dbname=postgres" \
--progress
关键参数说明:
--target-pgdata:指定需要被同步的目标数据目录(必须已停止)--source-pgdata:源集群数据目录路径(必须已停止)--source-server:源集群连接字符串(必须正在运行)--progress:显示进度信息(推荐始终启用)--dry-run:试运行而不实际修改(首次使用时建议先试运行)
3.2 完整操作流程演示
假设我们有以下场景:
- 原主库:192.168.1.100(已故障,恢复后需要重新加入)
- 新主库:192.168.1.101
- 目标:将原主库设置为新主库的从库
步骤1:停止目标实例
bash复制pg_ctl -D /var/lib/postgresql/12/main stop -m fast
步骤2:执行pg_rewind
bash复制pg_rewind \
--target-pgdata=/var/lib/postgresql/12/main \
--source-server="host=192.168.1.101 user=rewind_user dbname=postgres" \
--progress \
--write-recovery-conf
--write-recovery-conf参数会自动创建standby.signal文件并在postgresql.auto.conf中添加主库连接信息。
步骤3:验证并启动
检查目标数据目录:
bash复制ls -l /var/lib/postgresql/12/main/standby.signal
cat /var/lib/postgresql/12/main/postgresql.auto.conf
确认无误后启动实例:
bash复制pg_ctl -D /var/lib/postgresql/12/main start
步骤4:验证复制状态
在新主库上检查:
sql复制SELECT client_addr, state, sync_state FROM pg_stat_replication;
4. 常见问题排查与性能优化
4.1 典型错误与解决方案
错误1:目标集群未干净关闭
code复制target server must be shut down cleanly
解决方案:
bash复制pg_ctl -D /var/lib/postgresql/12/main start -m immediate
pg_ctl -D /var/lib/postgresql/12/main stop -m fast
错误2:缺少必需的WAL文件
code复制could not find previous WAL record
解决方案:
使用-c参数让pg_rewind从归档中获取缺失的WAL:
bash复制pg_rewind ... --restore-target-wal
错误3:权限不足
code复制permission denied for function pg_ls_dir
解决方案:确保连接用户具有前述的必要权限,或使用超级用户。
4.2 性能优化技巧
-
并行复制:PostgreSQL 12支持并行恢复,在postgresql.conf中设置:
code复制max_worker_processes = 8 max_parallel_maintenance_workers = 4 -
网络优化:在线模式时,确保网络带宽足够。对于跨机房同步,考虑先压缩传输:
bash复制
pg_rewind ... | pigz -c > rewind_stream.gz -
IO调度调整:在Linux上,为PostgreSQL数据目录所在磁盘设置deadline调度器:
bash复制echo deadline > /sys/block/sdb/queue/scheduler -
内存调整:临时增加shared_buffers用于rewind操作:
code复制shared_buffers = 4GB # 仅为rewind操作设置,完成后可恢复原值
5. 高级应用场景与限制
5.1 表空间特殊处理
如果数据库使用了表空间,pg_rewind需要额外注意:
- 确保所有表空间在源和目标集群上的挂载点一致
- 对于自定义表空间位置,使用
--sync-method=fsync(默认)确保数据安全 - 表空间中的临时文件会被自动排除,无需手动处理
5.2 部分rewind失败处理
如果pg_rewind执行中途失败,目标数据目录可能处于不一致状态。此时:
- 不要尝试重启PostgreSQL
- 立即备份pg_wal目录(可能包含重要WAL)
- 从源集群重新执行pg_rewind,或考虑全新基准备份
5.3 与逻辑复制的交互
当使用逻辑复制时需特别注意:
- 执行pg_rewind前暂停所有逻辑复制槽:
sql复制SELECT pg_replication_slot_advance(slot_name, restart_lsn); - rewind完成后重新创建逻辑复制槽
- 监控逻辑复制滞后情况,必要时重新同步订阅
5.4 工具限制与替代方案
pg_rewind并非万能,以下情况不适用:
- PostgreSQL大版本升级(如11→12)
- 不同架构间的数据同步(如x86到ARM)
- 源和目标集群的扩展(extension)版本不一致
在这些情况下,应考虑使用pg_dump/pg_restore或逻辑复制工具如Debezium。
