PostgreSQL的连续备份配置

需求定义

之前使用postgresql数据库备份是基于两种方式:一种是定期使用pg_dump作数据库的备份,另一种是用ZFS做硬盘快照备份。但是这两种方法都有其局限,前者在数据量大了以后太慢,后者的问题则是依赖于ZFS,而且也是有风险的。所以需要新的方案。

官方文档提供的方案是:基于WAL的连续归档。类似于我以前用过的ms sql或mysql的日志备份,提供了指定时间点恢复的能力。

实现方法

结合官方文档和GPT/Gemini的指导,备份和恢复需要以下步骤:

备份

  • 启用WAL归档
  • 生成基础备份(同时记录WAL中的检查点,以便未来使用WAL恢复),如有通过链接加入的表空间,也需要同时备份
  • 备份WAL归档

恢复

  • 停止服务
  • 备份现有数据文件(特别是未归档的WAL)
  • 删除现有数据文件(不要和上一步合来直接使用移动操作,以免出错导致数据不一致)
  • 恢复基础备份(包括链接的表空间)
  • 删除恢复出来的未归档WAL
  • 恢复第二步备份的未归档WAL
  • 配置postgresql.conf为恢复模式,从指定位置恢复归档过的WAL
  • 启动服务,自动开始恢复(注意配置pg_hba.conf,禁止期间有不必要的数据库操作)
  • 检查恢复结果

备份的具体配置

首先是启用WAL归档。修改postgresql.conf,加入以下内容:

archive_mode = on
archive_command = 'test ! -f /backup/archive_wal/%f && gzip < %p > /backup/archive_wal/%f && chmod g+r /backup/archive_wal/%f'
wal_level = replica
archive_timeout = 3600  # 每小时强制归档一次

其中的重点是archive_command,这里是一句shell命令,当它返回成功的时候,pgsql才会认为归档成功,并定期删除已归档成功的WAL。所以这里按官方文档作了一个测试,并且对要归档的WAL文件进行压缩。最后为了方便复制备份内容,加上了group的读权限,然后把复制备份的用户加入到postgres用户组(GID=70)中。

重启服务令这个配置生效后,准备做一个基础备份。

pg_basebackup -U postgres -h localhost -D /backup/$DBNAME -Ft -z -Xs -R

DBNAME变量为一个目录名,基础备份会在这里保存几个文件:包括数据库基础数据,未归档的WAL,以及一个描述文件记录校验信息等。参数中的-Ft -z表示以tar.gz形式压缩备份,否则以数据库原始数据方式备份。-Xs表示以stream方式记录WAL。-R表示生成recovery.conf文件以便恢复。

最后将这个基础备份目录和前面配置的那个/backup/archive_wal/中的归档WAL文件全部保存好即完成备份。

如果要定期重建基础备份的话,可以同时把旧的WAL归档删除以节约空间。

# 删除旧的基础备份
rm /backup/$(DBNAME)/*
if
    pg_basebackup -U postgres -h localhost -D /backup/$(DBNAME) -Ft -z -Xs -R
then
    # 必要时修改权限,以便其它用户复制备份
    chown 1000:1000 /backup/${DBNAME}/*
    # 基础备份成功则删除一天以前的WAL归档
    find ${BAKWAL}/ -type f -mtime +1 -exec rm {} \;
else
    # 基础备份失败则退出
    echo "pg_basebackup fail"
    exit 1
fi

恢复的具体操作

首先是停止现有服务并备份数据文件

systemctl stop postgresql
cp -R /var/lib/postgresql /var/backup/new
rm -r /var/lib/postgresql/*

恢复基础数据:

# 因为是使用压缩打包的格式,所以这里需要解压
cd /var/lib/postgresql
tar -xvf /var/backup/old/base.tar.gz

因为只恢复基础数据,不含WAL,所以可以略过删除未归档WAL的操作,如前面的备份中有未归档wal,则需要恢复过来:

cp -R /var/backup/new/pg_wal/* /var/lib/postgresql/pg_wal/

然后配置postgresql服务为恢复模式,修改postgresql.conf,其中的路径是存放归档WAL的地方:

restore_command = 'gunzip -c /backup/archive_wal/%f > %p'
recovery_target_timeline = 'latest'

注意,因为之前归档WAL时使用了gzip压缩,此处需要解压出来恢复。

另外,如果这个WAL是备份过的,要注意一下文件权限,必须是postgres用户(UID=70)可读。

还有就是如果原来有archive wal配置要先去掉。

修改pg_hba.conf,暂时禁止不必要的连接,然后启动postgresql服务:

systemctl start postgresql

最后检查一下恢复情况,日志里会显示恢复进度,直到恢复成功后即可停止服务,然后修改postgresql.confpg_hba.conf,恢复正常的配置,然后重启服务即可正常使用。

推送到[go4pro.org]