单虚拟机测试pitr恢复
本文记录在单台虚拟机上部署PostgreSQL 16双实例(main主库、test恢复库),并完成PITR(Point-in-Time Recovery,时间点恢复)的完整实操流程。
核心场景:单虚拟机内,通过两个PostgreSQL实例(main作为主库提供服务,test作为恢复库),实现主库备份、指定时间点恢复,最终将恢复库终止恢复、转为正常独立实例,模拟生产环境中数据恢复及实例切换的基础流程。(实际原因为我只有一台虚拟机)
一、环境说明
- 系统版本:Ubuntu(适配PostgreSQL 16)
- PostgreSQL版本:16.13(Ubuntu 16.13-0ubuntu0.24.04.1)
- 实例规划:
- main实例(主库):端口5432,用于数据存储、备份源
- test实例(恢复库):端口5433,用于接收主库备份、完成时间点恢复
- 核心目录:
- 归档目录:/var/lib/postgresql/16/archive(存储WAL归档日志)
- 备份目录:/backup(存储主库基础备份文件)
- 实例数据目录:/var/lib/postgresql/16/main(main实例)、/var/lib/postgresql/16/test(test实例)
二、前期准备(创建目录并授权)
创建归档目录和备份目录,确保postgres用户拥有完整权限,避免后续备份、归档失败。
# 创建归档目录(用于存储WAL归档日志)
postgres@lzy:~/16$ mkdir -p /var/lib/postgresql/16/archive
# 创建备份目录(用于存储主库备份)
postgres@lzy:~/16$ mkdir -p /backup
# 授权postgres用户为目录所有者(PostgreSQL服务默认以postgres用户运行)
postgres@lzy:~/16$ chown -R postgres:postgres /var/lib/postgresql/16/
# 授权postgres用户为备份目录所有者
postgres@lzy:~/16$ chown -R postgres:postgres /backup/
三、部署main主实例(主库)
创建main实例、启动服务,并配置WAL归档(需重启实例生效)。
# 创建main实例(PostgreSQL 16版本,实例名main)
postgres@lzy:~/16$ pg_createcluster 16 main
# 启动main实例
postgres@lzy:~/16$ pg_ctlcluster 16 main start
# 开启归档模式(ALTER SYSTEM会将配置写入postgresql.auto.conf,永久生效)
postgres@lzy:~/16$ psql -p 5432 -U postgres -c "ALTER SYSTEM SET archive_mode = on;"
# 配置归档命令:先检查文件是否存在,避免重复归档,再将WAL文件复制到归档目录
postgres@lzy:~/16$ psql -p 5432 -U postgres -c "ALTER SYSTEM SET archive_command = 'test ! -f /var/lib/postgresql/16/archive/%f && cp %p /var/lib/postgresql/16/archive/%f';"
# 重启main实例,使归档参数生效(archive_mode为静态参数,仅reload无法生效)
postgres@lzy:~/16$ pg_ctlcluster 16 main restart
四、main主库基础备份
切换WAL日志,确保归档起点干净,然后执行在线基础备份(pg_basebackup不影响主库正常运行)。
# 切换WAL日志,生成新的WAL文件,确保归档目录有完整的日志起点
postgres@lzy:~/16$ psql -p 5432 -U postgres -c "select pg_switch_wal();"
# 执行在线基础备份:端口5432(main实例),备份到/backup/full_backup,格式为tar压缩,同步获取WAL日志
postgres@lzy:~/16$ pg_basebackup -p 5432 -U postgres -D /backup/full_backup -F t -z -X fetch
五、main主库插入测试数据(用于验证恢复效果)
创建测试数据库、数据表,插入两组测试数据(数据A、数据B),并切换WAL日志确保数据归档,为后续时间点恢复做准备。
postgres@lzy:~/16$ psql -p 5432 -U postgres <<EOF
DROP DATABASE IF EXISTS prod_data;
CREATE DATABASE prod_data;
\c prod_data;
CREATE TABLE user_info(id int,name text,ctime timestamp);
INSERT INTO user_info VALUES(1,'数据A',now());
SELECT now();
EOF
NOTICE: database "prod_data" does not exist, skipping
DROP DATABASE
CREATE DATABASE
You are now connected to database "prod_data" as user "postgres".
CREATE TABLE
INSERT 0 1
now
-------------------------------
2026-04-15 15:18:49.593855+00
(1 row)
postgres@lzy:~/16$ psql -p 5432 -U postgres -c "select pg_switch_wal();"
pg_switch_wal
---------------
0/4475300
(1 row)
#插入数据B
postgres@lzy:~/16$ psql -p 5432 -U postgres <<EOF
\c prod_data;
INSERT INTO user_info VALUES(2,'数据B',now());
EOF
You are now connected to database "prod_data" as user "postgres".
INSERT 0 1
postgres@lzy:~/16$ psql -p 5432 -U postgres -c "select pg_switch_wal();"
pg_switch_wal
---------------
0/50000F0
(1 row)
#再次查看数据
postgres@lzy:~/16$ psql -p 5432 -U postgres -d prod_data -c "SELECT * FROM user_info;"
id | name | ctime
----+-------+----------------------------
1 | 数据A | 2026-04-15 15:18:49.590541
2 | 数据B | 2026-04-15 15:19:20.942403
(2 rows)
六、部署test恢复实例
创建test实例,停止实例并清空其数据目录,为后续恢复主库备份做准备。
# 创建test实例(PostgreSQL 16版本,实例名test,默认端口5433)
postgres@lzy:~/16$ pg_createcluster 16 test
# 停止test实例(后续需清空数据目录,避免原有数据干扰恢复)
postgres@lzy:~/16$ pg_ctlcluster 16 test stop
# 清空test实例数据目录(确保恢复环境干净)
postgres@lzy:~/16$ rm -rf /var/lib/postgresql/16/test/*
# 解压main主库的基础备份,恢复到test实例数据目录
postgres@lzy:~/16$ tar -zxf /backup/full_backup/base.tar.gz -C /var/lib/postgresql/16/test/
七、test实例执行PITR时间点恢复
配置test实例的恢复参数,指定恢复时间点(介于数据A和数据B插入时间之间),启动恢复并验证恢复效果。
# 配置test实例的恢复参数(追加到postgresql.conf末尾)
# restore_command恢复命令:从归档目录复制WAL日志到test实例的pg_wal目录
# recovery_target_time恢复目标时间:介于数据A插入时间15:18:49和数据B插入时间15:19:20之间
# 恢复完成后暂停,便于验证恢复结果
postgres@lzy:~/16$ cat >> /etc/postgresql/16/test/postgresql.conf <<EOF
restore_command = 'cp /var/lib/postgresql/16/archive/%f %p 2>/dev/null || true'
recovery_target_time = '2026-04-15 15:19:00'
recovery_target_action = pause
EOF
# 创建恢复信号文件(PostgreSQL检测到该文件,会进入恢复模式)
postgres@lzy:~/16$ touch /var/lib/postgresql/16/test/recovery.signal
# 授权postgres用户为test实例目录所有者,避免权限不足
postgres@lzy:~/16$ chown -R postgres:postgres /var/lib/postgresql/16/test/
# 启动test实例,开始执行时间点恢复
postgres@lzy:~/16$ pg_ctlcluster 16 test start
# 验证恢复结果:查看test实例的prod_data数据库,确认恢复情况
postgres@lzy:~/16$ psql -p 5433 -U postgres -d prod_data -c "SELECT * FROM user_info;"
id | name | ctime
----+-------+----------------------------
1 | 数据A | 2026-04-15 15:18:49.590541
(1 row)
#查看test数据库在恢复状态
postgres@lzy:~/16$ pg_lsclusters
Ver Cluster Port Status Owner Data directory Log file
16 main 5432 online postgres /var/lib/postgresql/16/main /var/log/postgresql/postgresql-16-main.log
16 test 5433 online,recovery postgres /var/lib/postgresql/16/test /var/log/postgresql/postgresql-16-test.log
# 暂停WAL重放(恢复模式下,先暂停再执行提升)
postgres@lzy:~/16$ psql -p 5433 -U postgres -c "select pg_wal_replay_pause();"
# 提升test实例为主库,终止恢复模式(转为正常实例)
postgres@lzy:~/16$ psql -p 5433 -U postgres -c "select pg_promote();"
八、注意事项
归档参数(archive_mode)为PostgreSQL静态参数,仅执行pg_reload_conf()无法生效,必须重启实例才能开启归档,若未重启,归档目录会为空,导致恢复失败。
九、总结
本文完成了单虚拟机上PostgreSQL 16双实例的部署、主库备份、PITR时间点恢复及恢复实例升主的完整实操,测试环境验证PITR恢复能力。核心关键点在于WAL归档的正确配置(需重启实例)、恢复时间点的准确设定。