研究了Postgres的库数据恢复和转移,再来研究一下PITR按时间点恢复

类比到MySQL,因为我们是运维,不是专业的DBA,所以恢复到正确数据即可

那跟阿里云以及自建的MySQL恢复一样,首先都要有一个每天的基础备份,然后类比MySQL的Binlog,Postgres这里就相当于wal。

这么类比就对了,那么恢复过程的思路是一样的:

  • 首先建立一个新的实例
  • 第二步把每天的基础物理全备给恢复上去
  • 第三步把之后的wal日志传到新实例,恢复到指定时间点
  • 第四步最后把表或库Dump下来,灌入到旧实例的新表或新库

那我们一步一步来解释:

一、每天建立一个基础备份,这样在Base基础上,只需恢复当天的wal,速度够快

建立每天的Base基础物理备份

pg_basebackup -Ft -Pv -Xf -z -Z5 -U postgres -h localhost -p 5432 -D /pg_backup

解释一下:
    -Ft:
        指定备份格式为 tar。
        如果使用 tar 格式,备份会输出 tar 文件,并且不需要直接访问 PostgreSQL 数据目录。
    -Pv:
        -P:显示备份进度。
        -v:启用详情模式(verbose),显示更多日志。
    -Xf:
        -X:控制 WAL 日志的处理方式,f 表示在备份中包含 WAL 文件(Write Ahead Logs),确保备份可以用于恢复到完整的一致状态。
    -z:
        启用压缩功能。
    -Z5:
        设置压缩级别为 5(压缩级别从 0 到 9,9 为最高压缩)。
    -U postgres:
        使用 postgres 用户来连接数据库。
    -h localhost:
        指定数据库主机为当前机器(localhost)。
    -p 5432:
        指定 PostgreSQL 监听端口为 5432(默认端口)。
    -D /pg_backup:
        将备份文件保存到指定路径 /pg_backup 中。

这样我们就在 /pg_backup 中有了备份文件,包括一个backup_manifest的文件清单,以及一个压缩包

root@debian:/pg_backup# ls -lha
total 1.8G
drwxr-xr-x  3 root root 4.0K Jul 28 16:31 .
drwxr-xr-x 20 root root 4.0K Jul 28 16:18 ..
-rw-------  1 root root 760K Jul 28 16:21 backup_manifest
-rw-------  1 root root 1.8G Jul 28 16:21 base.tar.gz

那这个base.tar.gz里面又都有什么呢?

/
├── base/                   # 每个数据库的文件
├── global/                 # 全局对象(角色、权限)
├── pg_wal/                 # WAL 日志
├── pg_xact/                # 事务状态
├── pg_clog/                # 事务提交信息
├── postgresql.auto.conf    # 自动配置文件
├── postgresql.conf         # 主配置文件
├── pg_hba.conf             # 访问控制文件
├── pg_ident.conf           # 用户映射文件
└── ...                     # 其他系统文件

我们先 ps axjf 看一把

image-20250728172232116

可以看到,-D的数据目录是在/var/lib/postgresql/13/main下,那base.tar.gz里面的内容跟/var/lib/postgresql/13/main下是一样的。

二、旧实例需要建立wal的归档模式
# ps axjf的结果中,看到配置文件是 /etc/postgresql/13/main/postgresql.conf
# 那就修改这个配置文件

# 修改以下2行
# 启用归档
archive_mode = on
# WAL 日志归档路径
archive_command = 'cp %p /path_to_archive_directory/%f'

# 然后重启服务生效
systemctl restart postgresql

以上做完后我们如果我们做了Base备份,然后wal的日志还在继续跑,那我们就需要手动触发一下wal写盘

select pg_switch_wal();

 pg_switch_wal
---------------
 0/13DAC308

会得到一个序列号:0/13DAC308

然后新的wal归档日志就放在:/path_to_archive_directory/目录下了,那原始的wal数据文件还在 /var/lib/postgresql/13/main/pg_wal 下

三、新实例恢复Base库和时间点:
# 找到新实例的数据目录,一把删空,然后把base.tar.gz解压进去
# 建恢复目录并清理旧数据
# 将解压后的备份移到数据库实例的实际数据目录(通常位于 /var/lib/postgresql/data)。清理旧目录后执行:
mv /pg_restore/ /var/lib/postgresql/data

# 设置恢复配置文件
# 在数据目录中创建一个名为 recovery.signal 的空文件,同时在数据目录中新增恢复目标的配置文件 postgresql.auto.conf。
touch recovery.signal

#编辑或创建文件 postgresql.auto.conf:
vi /var/lib/postgresql/data/postgresql.auto.conf

# 添加以下内容:
# 旧实例的wal文件需要事先copy到新实例的 /path_to_archive_directory 目录下
primary_conninfo = ''
restore_command = 'cp /path_to_archive_directory/%f %p'
recovery_target_time = 'YYYY-MM-DD HH:MI:SS'
recovery_target_action = 'pause'

说明:

restore_command:用于指定如何恢复 WAL 日志。这里的命令是从归档目录 /path_to_archive_directory/ 中拷贝 WAL 日志到 PostgreSQL 数据目录。

recovery_target_time:你希望恢复到的时间点精确到秒(UTC 时间)。

recovery_target_action:在完成恢复到目标时间后,pause 表示暂停恢复并进入只读模式

然后开始恢复:

# 确保 PostgreSQL 数据目录已准备好,删除目录下旧的 postmaster.pid 等文件:
rm /var/lib/postgresql/data/postmaster.pid

# 启动 PostgreSQL 服务:
systemctl start postgresql

# 确认恢复进度
# 观察 PostgreSQL 的日志文件(通常位于 /var/log/postgresql/postgresql-X.log,具体路径根据配置文件可能不同):
tail -f /var/log/postgresql/postgresql-X.log

# 如果一切正常,你应该能看到以下内容:
consistent recovery point reached at 0/16B6F98
recovery stopping at specified recovery target

# 退出恢复模式
mv /var/lib/postgresql/data/standby.signal /var/lib/postgresql/data/standby.signal.bak

# 退出恢复模式后,确认是只读模式
SELECT pg_is_in_recovery();

# 如果要进入可读可写的模式
select pg_wal_replay_resume();
四、dump出新实例指定表或库的数据,然后灌回旧实例的新表或新库

这个过程就不多说了。

多说一句,可以恢复到某个时间点,事务或者wal序列号

# 恢复到不同时间点
# 假设第一次恢复后,你希望尝试恢复到另一个时间点,修改 postgresql.auto.conf 文件中的 recovery_target_time 然后同样重复以上步骤即可重新恢复。
recovery_target_time = 'YYYY-MM-DD HH:MI:SS'

# 以某个事务为目标:
recovery_target_xid = '12345'

#以某个 WAL 日志序列号为目标:
recovery_target_lsn = '0/16B6F98'

总结一下:无论postgres还是mysql,一键恢复的功能,最好都是新起一个实例,然后把基础备份恢复上去,然后用binlog或wal归档日志进行恢复,然后导出数据,最后灌入到旧实例中,这样最稳妥,旧实例也不会停服务!!!