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

标签

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 最佳实践 - 在线增量备份与任意时间点恢复》

时间: 2024-09-19 08:57:23

PostgreSQL 增量备份集的有效恢复位点的相关文章

mysldump数据的完全备份+增量备份+故障后的恢复

学习了几天mysql的知识.刚接触单单以为是语法之类的命令记住就行了.但是遇到问题的时候缺不知道从何下手.网上找了相关的资料感觉还是不太实用.于是笔者就着手总结了备份与还原的例子.这篇是mysqldump的使用.进行了2个事例的操作.当然两个事例大同小异.第二个为第一个的拓展.如果将这2个例子原理搞清楚了.接下来进行LVM备份还原以及Xtrabackup这个备份工具都会轻松很多. MySQL的备份方式 前提:事先保证对备份类型,备份策略等mysql常用知识已经清楚. mysqldump: 逻辑备

PostgreSQL 最佳实践 - 块级增量备份(ZFS篇)多zfs卷场景一致性备份

背景 当我们使用了多个ZFS卷或者文件系统时,如果一个实例的多个部分,如表空间,放在了不同的zfs上,再使用基于ZFS快照的备份时,可能出现多个文件系统不一致的情况. 例如控制文件是新的,但是数据是旧的. 保物理备份的一致性检查 基于文件的物理备份,为了保证备份的一致性,在备份开始时,需要做一个检查点,同时打开FULL PAGE WRTIE,同时还会生成backup_label文件记录备份开始时的WAL文件,检查点位置等信息. backup_label文件内容示例 START WAL LOCAT

【MySql】innobackupex 增量备份和恢复

   innobackupex 是使用pl封装了xtrabackup之后的工具,在使用的时候会调用xtrabackup. 1 创建环境 -bash-3.2$ mysql mysql> use test;                                Database changed mysql> select count(1) from t1;                           +----------+ | count(1) | +----------+ |

【RAC】将RAC备份集恢复为单实例数据库

[RAC]将RAC备份集恢复为单实例数据库 1.1  BLOG文档结构图   1.2  前言部分   1.2.1  导读 各位技术爱好者,看完本文后,你可以掌握如下的技能,也可以学到一些其它你所不知道的知识,~O(∩_∩)O~: ① rac数据库的备份集是如何恢复到单实例的数据库 ② ASM文件系统到OS文件系统的转换 ③ 一般的备份恢复过程       本文如有错误或不完善的地方请大家多多指正,ITPUB留言或QQ皆可,您的批评指正是我写作的最大动力. 1.2.2  实验环境介绍   源库:1

【RAC】将单实例备份集恢复为rac数据库

[RAC]将单实例备份集恢复为rac数据库 一.1  BLOG文档结构图     一.2  前言部分   一.2.1  导读 各位技术爱好者,看完本文后,你可以掌握如下的技能,也可以学到一些其它你所不知道的知识,~O(∩_∩)O~: ① 单实例环境的备份集如何恢复到rac环境(重点) ② rman恢复数据库的一般步骤 ③ rac环境的简单操作   注意:本篇BLOG中代码部分需要特别关注的地方我都用黄色背景和红色字体来表示,比如下边的例子中,thread 1的最大归档日志号为33,thread

差异增量备份和累积增量备份的差别

差异增量备份和累积增量备份的差别 差异增量备份,会备份自上次同级或低级差异增量备份以来所有发生变化的数据块 累积增量备份,会备份自上次0级备份以来发生变化的数据块. 也就是他们的差异主要在于起点不同.因为累积备份是直接从0级开始的,它的数据量较大,占用空间多,如果要恢复,花费的时间相对较少. 先做一个0级备份 RMAN> backup incremental level=0 database; -- handle=/u01/app/oracle/flash_recovery_area/VM62/

Oracle增量备份和快速备份(块改变跟踪Block Change Tracking)

Oracle增量备份和快速备份(块改变跟踪Block Change Tracking) 下面小麦苗给出全库备份的脚本: 点击(此处)折叠或打开 [oracle@rhel6lhr ~]$ crontab -l 2 12 * * 1 /home/oracle/lhr/rman/rman_backup_full.sh [oracle@rhel6lhr ~]$ more /home/oracle/lhr/rman/rman_backup_full.sh export ORACLE_SID=orclasm

DELETE OBSOLETE不删除归档日志以及归档的备份集

今天遇到一个奇怪的事情,使用OBSOLETE不删除归档日志,而且也不删除过期的归档的BACKUP SET 从delete obsolete的概念来看如下: The REPORT OBSOLETE and DELETE OBSOLETE commands work in two steps:                                                                                                         

使用mysqldump导入数据和mysqldump增量备份(mysqldump使用方法)_Mysql

 1.各种用法说明 A. 最简单的用法: 复制代码 代码如下: mysqldump -uroot -pPassword [database name] > [dump file] 上述命令将指定数据库备份到某dump文件(转储文件)中,比如: 复制代码 代码如下: mysqldump -uroot -p123 test > test.dump 生成的test.dump文件中包含建表语句(生成数据库结构哦)和插入数据的insert语句. B. --opt 如果加上--opt参数则生成的dump文