PostgreSQL 增量备份集的有效恢复位点

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云原生数据库 PolarDB 分布式版,标准版 2核8GB
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介:

标签

PostgreSQL , 物理备份 , 时间点恢复 , PITR , 增量备份 , 归档 , 一致性 , 逻辑检查点 , 时间线


背景

PostgreSQL支持PITR即时间点恢复,为了支持时间点恢复,至少需要一次全量备份,然后需要归档日志。

这句话描述可能不够清晰,至少需要哪些归档日志,全量备份的时间点有没有要求呢?

本文要解答这个问题。

什么是全量备份

全量备份指的是对数据库的$PGDATA以及所有表空间文件(包括全局数据文件、事务日志文件、配置文件、控制文件、表空间数据文件等)进行一次全量的拷贝。

一个数据库的目录结构通常如下

cd $PGDATA  
drwx------ 6 digoal digoal 4.0K Aug 13 08:23 base  
-rw------- 1 digoal digoal   44 Aug 23 00:00 current_logfiles  
drwx------ 2 digoal digoal 4.0K Aug 16 11:28 global  
drwx------ 2 digoal digoal 4.0K Aug 23 00:00 log  
drwx------ 2 digoal digoal 4.0K Aug 13 07:25 pg_commit_ts  
drwx------ 2 digoal digoal 4.0K Aug 13 07:25 pg_dynshmem  
-rw------- 1 digoal digoal 4.5K Aug 13 07:25 pg_hba.conf  
-rw------- 1 digoal digoal 1.6K Aug 13 07:25 pg_ident.conf  
drwx------ 4 digoal digoal 4.0K Aug 23 10:18 pg_logical  
drwx------ 4 digoal digoal 4.0K Aug 13 07:25 pg_multixact  
drwx------ 2 digoal digoal 4.0K Aug 16 11:28 pg_notify  
drwx------ 2 digoal digoal 4.0K Aug 13 07:25 pg_replslot  
drwx------ 2 digoal digoal 4.0K Aug 13 07:25 pg_serial  
drwx------ 2 digoal digoal 4.0K Aug 13 07:25 pg_snapshots  
drwx------ 2 digoal digoal 4.0K Aug 16 11:28 pg_stat  
drwx------ 2 digoal digoal 4.0K Aug 23 13:57 pg_stat_tmp  
drwx------ 2 digoal digoal  20K Aug 14 13:13 pg_subtrans  
drwx------ 2 digoal digoal 4.0K Aug 13 07:25 pg_tblspc  
drwx------ 2 digoal digoal 4.0K Aug 13 07:25 pg_twophase  
-rw------- 1 digoal digoal    3 Aug 13 07:25 PG_VERSION  
drwx------ 3 digoal digoal 1.3M Aug 23 10:18 pg_wal  
drwx------ 2 digoal digoal 4.0K Aug 14 22:00 pg_xact  
-rw------- 1 digoal digoal 2.2K Aug 13 07:26 postgresql.auto.conf  
-rw------- 1 digoal digoal  23K Aug 16 11:25 postgresql.conf  
-rw------- 1 digoal digoal   34 Aug 16 11:28 postmaster.opts  
-rw------- 1 digoal digoal   90 Aug 16 11:28 postmaster.pid  

注意pg_tblspc里面是软链接,这里面对应的是表空间目录。也需要备份。

全量备份可以通过COPY文件的方式,给文件系统、块设备打快照的方式,等进行全量的备份。

COPY文件可以使用操作系统的命令(如果这么做,建议你考虑到软链接的问题,一定要记得备份实际的文件)。

如果是远程备份,可以配置数据库的流复制,通过pg_basebackup命令进行流式的备份,这样你不需要考虑表空间需要单独备份的问题,pg_basebackup会帮你做掉。

不管何种方式备份(除了pg_basebackup),都需要你执行pg_start_backup(''),这样数据库会做一次检查点,同时强制打开full page write(确保即使某些用户关闭了full page write,备份还是有效的。),拷贝完后,执行pg_stop_backup()。注意pg_stop_bacup()之前,你的备份是无效的。所以备份完成一定要记得pg_stop_backup()。后面会说为什么要这么做。这些步骤pg_basebackup会自动帮你做。

什么是不一致拷贝

因为全量备份是不需要停库,也不影响业务的。属于热备份。

热备份比如会带来一个问题,例如用户在写数据,数据库在刷脏页等操作,你在备份时可能拷贝走的文件是partial block,一个块中有一半新的一半旧的数据。造成不一致。

不过你不需要担心这个不一致的问题,因为PG考虑到了,并且有方法解决它。

这也是为什么需要执行pg_start_backup(),开启full page write的原因。开启full page write后,检查点之后,任何一个BLOCK第一次变成dirty block时,都会往WAL里面写下完整的数据块。

通过wal的完整数据块,可以修复备份过程中拷贝走的不一致数据块。

什么是一致性位点

既然备份走的文件里有不一致的数据块,以及PG有不一致的修复方法。那么就一定有一致的位点。

什么是一致的位点呢?就是指数据库认为所有的数据块都是一致的,没有partial write(一半新、一半旧)的情况。

什么是检查点,数据库DOWN机、服务器DOWN机如何恢复到一致性状态

检查点是数据库的一致性点,做检查点的目的是将SHARED BUFFER中的脏页刷到磁盘中持久化。同时开启full page write的情况下,检查点之后第一次变成dirty block时,都会往WAL里面写下完整的数据块。

做检查点可能需要一定的时间,这段时间随着数据库的读写,会产生一些WAL,因此检查点对应到WAL文件中,有一个开始位置和结束位置,比如开始位置在WAL文件A中,结束位置在WAL文件F中。

当数据库服务器异常DOWN机时,是需要恢复的。从数据库最后一次完成的检查点的WAL开始位置获取WAL开始恢复,一直恢复到检查点的逻辑位置结束位置为止。

即要恢复到一致位点,需要从A到F(CKPT的结束RECORD),只有到了这里,数据库才是一致的状态。

备份集如何恢复到一致性位点

那么备份集如何达到一致位点呢?

其实原理和检查点差不多,通过全量备份集来恢复,至少也要恢复到pg_stop_backup()的位置。

例如早上9点开始全量备份,早上11点备份结束,备份期间(9到11点)产生了A-F这些WAL文件。

那么这个备份集必须要包含A-F这些文件,才能恢复到一致性的位点。

为什么这么说,你想象一下,假设你在10点59快要备份结束的时候,数据库产生了一个脏页,并write到磁盘,此时你刚好拷贝到了这个partial block。而这个BLOCK是最后一次检查点之后第一次变更的BLOCK,那么你的备份集如果要恢复到一致性位点,必须使用F这个文件内(包含了这个块的FULL PAGE)来恢复这个BLOCK到一致的状态。

是不是很好理解呢?

什么情况下数据库会处于recovery状态不起来

当数据库在恢复时,如果没有达到一致性的状态(即前面提到的,没有恢复到必要的WAL位点)时,数据库会处于recovery状态无法连接。即使使用了standby模式,也一样。

采取什么措施

1. 继续获取更多的WAL,并恢复WAL,直到恢复到一致性点的WAL。(例如检查点逻辑位置,或者全量备份pg_stop_backup()的位置)。

2. 如果你没有足够的wal可用来恢复(无法达到一致性位点),怎么办呢?你可以强制promote激活。但是有可能遇到块错误的风险。当读取到这类数据块时会报错,可以使用zero demage block隐藏参数来跳过它。或者使数据库将这些块设置为INIT状态(使用vacuum freeze可以处理它)。

为什么检查点不适合跨度太大

postgresql有参数控制检查点的跨度,检查点做得太快(跨度小),检查点做得慢(跨度大)。

当数据库很繁忙时,一个检查点可能会跨越若干个WAL文件,为了到达一致性的位点,至少需要APPLY这些跨越的WAL文件才行。

最好的办法:

数据库产生脏页多,并且很繁忙时,跨度大一点,避免检查点引入的FSYNC IO开销影响业务。

数据库产生的脏页小,不繁忙时,跨度小一点,让数据库快速的到达一致性状态。

好在PostgreSQL 9.6开始就支持根据负载动态的调整检查点的跨度了。

PITR(时间点恢复)一致性的要素总结

1、数据库有两个一致性的点,检查点结束位点、全量备份的pg_stop_backup()位点。

为了让数据库可以恢复到一致性位点,需要足够的WAL,恢复到这两个位点以上,数据库才是一致的状态。

开启full page write,并且做好WAL归档是非常重要的,确保你在任意时候都可以恢复到一致的状态。

2、不要让检查点的时间太长,这样可以让数据库快速达到一致性状态。(PG 9.6已经支持动态CKPT调度,很棒吧)

3、recovery.conf中有一个参数,恢复到一致性的点即停止并pause或promote,是一个很不错的developor参数。

什么是时间线、恢复时如何利用时间线文件

时间线是数据库promote的时候产生的,当PostgreSQL的standby节点从只读节点变成读写节点时,会自动创建一个时间线文件。时间线文件是用于标记数据库是什么时候激活的。

时间线文件中包含了新时间线的第一条WAL记录的位置,也即是上一个时间线的最后一笔WAL RECORD的结束位置。

cat 00000002.history   
1       660/95B6F2A0    no recovery target specified --- 意思是恢复到这里,你就不要再恢复时间线1的WAL了。请切到时间线2。  

分析时间线1切换时的WAL文件

pg_waldump 000000010000066000000095|less

rmgr: Heap        len (rec/tot):    911/   911, tx:  474860760, lsn: 660/95B66B80, prev 660/95B66B58, desc: HOT_UPDATE off 12 xmax 474860760 ; new off 14 xmax 0, blkref #0: rel 1663/13146/2619 blk 9655  
rmgr: Heap        len (rec/tot):     54/    54, tx:  474860760, lsn: 660/95B66F10, prev 660/95B66B80, desc: LOCK off 13: xid 474860760: flags 0 LOCK_ONLY EXCL_LOCK , blkref #0: rel 1663/13146/2619 blk 9655  
rmgr: Heap        len (rec/tot):     73/ 31477, tx:  474860760, lsn: 660/95B66F48, prev 660/95B66F10, desc: UPDATE off 13 xmax 474860760 ; new off 13 xmax 0, blkref #0: rel 1663/13146/2619 blk 9658 FPW, blkref #1: rel 1663/13146/2619 blk 9655  
rmgr: Btree       len (rec/tot):     64/    64, tx:  474860760, lsn: 660/95B6EAA0, prev 660/95B66F48, desc: INSERT_LEAF off 1384, blkref #0: rel 1663/13146/2696 blk 126  
rmgr: Heap        len (rec/tot):   1251/  1251, tx:  474860760, lsn: 660/95B6EAE0, prev 660/95B6EAA0, desc: HOT_UPDATE off 10 xmax 474860760 ; new off 11 xmax 0, blkref #0: rel 1663/13146/2619 blk 9657  
rmgr: Heap        len (rec/tot):    184/   184, tx:  474860760, lsn: 660/95B6EFC8, prev 660/95B6EAE0, desc: INPLACE off 14, blkref #0: rel 1663/13146/1259 blk 0  
rmgr: Transaction len (rec/tot):     34/    34, tx:  474860760, lsn: 660/95B6F080, prev 660/95B6EFC8, desc: COMMIT 2017-08-23 09:46:13.592320 CST  
rmgr: XLOG        len (rec/tot):    106/   106, tx:          0, lsn: 660/95B6F0A8, prev 660/95B6F080, desc: CHECKPOINT_ONLINE redo 660/95B6F0A8; tli 1; prev tli 1; fpw true; xid 0:474860761; oid 683262; multi 1; offset 0; oldest xid 274864396 in DB 13146; oldest multi 1 in DB 13146; oldest/newest commit timestamp xid: 0/0; oldest running xid 0; online  
rmgr: XLOG        len (rec/tot):    106/   106, tx:          0, lsn: 660/95B6F118, prev 660/95B6F0A8, desc: CHECKPOINT_SHUTDOWN redo 660/95B6F118; tli 1; prev tli 1; fpw true; xid 0:474860761; oid 675073; multi 1; offset 0; oldest xid 274864396 in DB 13146; oldest multi 1 in DB 13146; oldest/newest commit timestamp xid: 0/0; oldest running xid 0; shutdown  
rmgr: XLOG        len (rec/tot):    106/   106, tx:          0, lsn: 660/95B6F188, prev 660/95B6F118, desc: CHECKPOINT_SHUTDOWN redo 660/95B6F188; tli 1; prev tli 1; fpw true; xid 0:474860761; oid 675073; multi 1; offset 0; oldest xid 274864396 in DB 13146; oldest multi 1 in DB 13146; oldest/newest commit timestamp xid: 0/0; oldest running xid 0; shutdown  
rmgr: XLOG        len (rec/tot):     50/    50, tx:          0, lsn: 660/95B6F1F8, prev 660/95B6F188, desc: PARAMETER_CHANGE max_connections=1000 max_worker_processes=128 max_prepared_xacts=0 max_locks_per_xact=6400 wal_level=replica wal_log_hints=off track_commit_timestamp=off  
rmgr: XLOG        len (rec/tot):    106/   106, tx:          0, lsn: 660/95B6F230, prev 660/95B6F1F8, desc: CHECKPOINT_SHUTDOWN redo 660/95B6F230; tli 1; prev tli 1; fpw true; xid 0:474860761; oid 675073; multi 1; offset 0; oldest xid 274864396 in DB 13146; oldest multi 1 in DB 13146; oldest/newest commit timestamp xid: 0/0; oldest running xid 0; shutdown  
----- 这是上一个时间线文件的内容,最后一条即时间线文件中的前一条RECORD。  

分析时间线2的第一个WAL文件。

pg_waldump 000000020000066000000095|less

rmgr: XLOG        len (rec/tot):     50/    50, tx:          0, lsn: 660/95B6F1F8, prev 660/95B6F188, desc: PARAMETER_CHANGE max_connections=1000 max_worker_processes=128 max_prepared_xacts=0 max_locks_per_xact=6400 wal_level=replica wal  
_log_hints=off track_commit_timestamp=off  
rmgr: XLOG        len (rec/tot):    106/   106, tx:          0, lsn: 660/95B6F230, prev 660/95B6F1F8, desc: CHECKPOINT_SHUTDOWN redo 660/95B6F230; tli 1; prev tli 1; fpw true; xid 0:474860761; oid 675073; multi 1; offset 0; oldest xid 27  
4864396 in DB 13146; oldest multi 1 in DB 13146; oldest/newest commit timestamp xid: 0/0; oldest running xid 0; shutdown  
-----这条之前的WAL都是继承自上一个时间线的,000000020000066000000095就是从000000010000066000000095复制出来的文件。  
rmgr: XLOG        len (rec/tot):     42/    42, tx:          0, lsn: 660/95B6F2A0, prev 660/95B6F230, desc: END_OF_RECOVERY tli 2; prev tli 1; time 2017-08-23 16:48:43.384886 CST  
rmgr: Standby     len (rec/tot):     50/    50, tx:          0, lsn: 660/95B6F2D0, prev 660/95B6F2A0, desc: RUNNING_XACTS nextXid 474860761 latestCompletedXid 474860760 oldestRunningXid 474860761  
rmgr: Standby     len (rec/tot):     50/    50, tx:          0, lsn: 660/95B6F308, prev 660/95B6F2D0, desc: RUNNING_XACTS nextXid 474860761 latestCompletedXid 474860760 oldestRunningXid 474860761  
rmgr: XLOG        len (rec/tot):    106/   106, tx:          0, lsn: 660/95B6F340, prev 660/95B6F308, desc: CHECKPOINT_ONLINE redo 660/95B6F2D0; tli 2; prev tli 2; fpw true; xid 0:474860761; oid 675073; multi 1; offset 0; oldest xid 274864396 in DB 13146; oldest multi 1 in DB 13146; oldest/newest commit timestamp xid: 0/0; oldest running xid 474860761; online  
rmgr: Standby     len (rec/tot):     50/    50, tx:          0, lsn: 660/95B6F3B0, prev 660/95B6F340, desc: RUNNING_XACTS nextXid 474860761 latestCompletedXid 474860760 oldestRunningXid 474860761  

recovery.conf文件中包含了对时间线的这样一段描述。

当用户恢复数据库时,如果要恢复到当前控制文件更大的时间线,(即跨时间线恢复,恢复到另一个激活的数据库),那么需要设置为latest。

# If you want to recover into a timeline other than the "main line" shown in  
# pg_control, specify the timeline number here, or write 'latest' to get  
# the latest branch for which there's a history file.  
#  
#recovery_target_timeline = 'latest'  

在了解了时间线的原理后,我们就需要注意一件事情。

如果你的系统中有非同步流复制关系的主备,并且主备都有归档文件时,千万不要搞错了他们的关系。

例如

1、A是主库

2、B是异步备库

3、A产生了一堆WAL归档。

4、某一时刻t1,做了一个全量备份

5、之后的某一时刻t2,HA程序认为A挂了,把B激活成为新的主库,激活时产生了几个文件:上一个时间线的最后一个WAL,新时间线的第一个WAL,以及一个新时间线文件,告诉你切换的WAL位点是,1 660/95B6F2A0 no recovery target specified,然后

6、由于是异步模式,A还有一些WAL没有发给B。

7、用户想恢复到t3的某一时刻,

8、如果你需要用老的备份集恢复到t3,那么就涉及到跨时间线恢复。注意当恢复需要用到B激活时的临界WAL文件时,千万不要使用A归档的临界WAL文件来恢复,否则会在老的时间线越走越远。

9、为了让恢复走上新时间线的道路,需要具备B上面产生的三个文件:上一个时间线的最后一个WAL,新时间线的第一个WAL,以及一个新时间线文件。

建议:

在切换时间线后,使用新的主库做一次全量备份。

数据库备份、恢复、容灾最佳实践文档

《PostgreSQL on ECS多云盘的部署、快照备份和恢复》

《PostgreSQL 最佳实践 - 块级增量备份(ZFS篇)验证 - recovery test script for zfs snapshot clone + postgresql stream replication + archive》

《PostgreSQL 最佳实践 - 块级增量备份(ZFS篇)双机HA与块级备份部署》

《PostgreSQL 最佳实践 - 块级增量备份(ZFS篇)单个数据库采用多个zfs卷(如表空间)时如何一致性备份》

《PostgreSQL 最佳实践 - 块级增量备份(ZFS篇)备份集自动校验》

《PostgreSQL 最佳实践 - 块级增量备份(ZFS篇)方案与实战》

《PostgreSQL 最佳实践 - 块级别增量备份(pg_rman baseon LSN)源码浅析与使用》

《PostgreSQL 最佳实践 - 任意时间点恢复源码分析》

《PostgreSQL 最佳实践 - 在线增量备份与任意时间点恢复》

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
3月前
|
存储 关系型数据库 Java
polardb有没有搞过pg 全量及增量备份管理的
【1月更文挑战第3天】【1月更文挑战第11篇】 polardb有没有搞过pg 全量及增量备份管理的
34 1
|
4月前
|
关系型数据库 数据库 PostgreSQL
postgresql|【基于pg_basebackup命令的归档备份和恢复---热备冷恢复方式】
postgresql|【基于pg_basebackup命令的归档备份和恢复---热备冷恢复方式】
50 0
|
2月前
|
Kubernetes 关系型数据库 分布式数据库
PolarDB for PostgreSQL备份问题之备份损坏如何解决
PolarDB for PostgreSQL是基于PostgreSQL开发的一款云原生关系型数据库服务,它提供了高性能、高可用性和弹性扩展的特性;本合集将围绕PolarDB(pg)的部署、管理和优化提供指导,以及常见问题的排查和解决办法。
|
3月前
|
关系型数据库 数据库 PostgreSQL
PostgreSQL从小白到高手教程 - 第41讲:postgres表空间备份与恢复
PostgreSQL从小白到高手教程 - 第41讲:postgres表空间备份与恢复
137 1
|
4月前
|
关系型数据库 数据库 PostgreSQL
postgresql|数据库|恢复备份的时候报错:pg_restore: implied data-only restore的处理方案
postgresql|数据库|恢复备份的时候报错:pg_restore: implied data-only restore的处理方案
63 0
|
4月前
|
安全 关系型数据库 数据库
postgresql|数据库|基于本地备份的远程备份策略
postgresql|数据库|基于本地备份的远程备份策略
53 0
|
4月前
|
SQL 关系型数据库 MySQL
postgresql |数据库 |数据库的常用备份和恢复方法总结
postgresql |数据库 |数据库的常用备份和恢复方法总结
109 0
|
4月前
|
存储 SQL 关系型数据库
postgresql从入门到精通 - 第37讲:postgres物理备份和恢复概述
postgresql从入门到精通 - 第37讲:postgres物理备份和恢复概述
160 1
|
4月前
|
存储 人工智能 关系型数据库
postgresql从入门到精通教程 - 第36讲:postgresql逻辑备份
postgresql从入门到精通教程 - 第36讲:postgresql逻辑备份
145 1
|
6月前
|
存储 关系型数据库 Go
《PostgreSQL备份与恢复:步骤与最佳实践》
《PostgreSQL备份与恢复:步骤与最佳实践》
354 0

相关产品

  • 云原生数据库 PolarDB