开发者学堂课程【7天突破PolarDB for PostgreSQL 2022版:备份与恢复实践】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/992/detail/14979
备份与恢复实践
内容介绍:
一、 basebackup 简介
二、如何实现完全和不完全恢复
三、PG_RMAN 备份工具(第三方)
一、basebackup 简介
1.Base Backup 基本备份的标准过程如下所示:
(1) lssue the pg_start_backup command
(2) Take a snapshot of thedatabase cluster with the archiving command you want touse
(3)lssue the pg_stop_backupcommand
假设数据库在运行中要进行备份,这种备份方式叫作热备份,如果要采用热备份,为了在备份过程中保证数据库是可恢复的。在备份时,PolarDB 首先需要调用 pg_start_backup 这个函数,然后对这个数据库做一个快照,做一个备份,备份完成后我们发出一个命令,叫做 pg_stop_backup 。
如果有 orcal 备份恢复的经历,知道 orcal 在热备份的时候,也会发一个 begin backup 和 end baclup。这两者之间实际上有很多相通之处。
当我们执行 pg_start_backup 时,数据库做哪些操作及为什么。
(1)pg_start_backup
pg_start_backup 执行以下四个操作:
Force into the full-page wirte mode.
Switch to the current WAL segment file (version 8.4 or later).
Do checkpoint.
Create a backup label file-
此文件创建于 base 目录的同一层,包含有关基本备份本身的基本信息,例如此检查点的检查点位置。
①四个操作解释如下:
首先将数据库变为强制的 full-page 模式
(注意:full-page wirte 就是把数据块的内容写到 redo log 文件中,也就是 PG 的wal 文件中,要注意正常情况下数据块将内容写的数据文件上,而不会写的日志,日志中实际记录的都是事务的信息,但是在备份时为了避免块不一致,必须要将该数据块写到 redo 。块不一致要如何理解,为什么会块不一致?
举个例子,PolarDB 的数据块默认为8k,在操作系统中,数据块是由 1k 组成,换句话说,现在一个数据库的块实际上就由8个操作系统块组成。假设有一个数据库的数据块,由若干个操作系统组成,在热备份时,操作系统在备份该数据库时,是根据该数据块的操作系统的块进行备份。可以想象,假如先备份一部分操作系统的块,该数据块的状态为1001,在 cope 时,备份完该部分其状态也为1001。但是因为该数据库是打开的,有可能在备份该块的时候,这个块被一个新的事务修改,修改后该数据库的状态发生改变,可能变为1002,所以在下一次去进行备份另一部分数据块时,备份这部分的数据块状态为1002。这样备份后的数据块前后不一致,用不一致的数据块做恢复时会产生错误。那么PolarDB 是怎么样去阻止该情况的发生呢?凡是在备份期间如果有数据库被改变,PolarDB 就会把整个块写到 wal 日志中。在将来做恢复时,就从 wal 中把该数据块恢复出来,这样就可以避免块不一致的问题。)
接下来会把当前的 wal 日志做切换(切换的目的是为了记录当前新的 wal 日志,记录新的事务)
然后做一个检查点(注意该检查点实际上是用其来记录当前备份的位置,将来从该位置开始恢复)
最后会产生一个 Create a backup label 文件,该文件作用是将检查点的位置记录和当前开始恢复时要用到哪一个日志文件的记录
②backup_label 文件包含如下内容(内容会在备份时产生,备份完删除):
CHECKPOINT LOCATION-这是记录此命令创建的检查点的LSN位置。
START WAL LOCATION-这不与PITR一起使用,而是与流式复制一起使用。
它被命名为 “START WAL LOCATION”,因为处于复制模式的备用服务器在初始启动时只读取一次该值。
BACKUP METHOD-这是用于进行此基础备份的方法。(pg_start _backup 或 pg basebackup。)
BACkUP FROM-这显示此备份是从主备份还是从备用备份。
START TIME一这是执行 pg start backup 时的时间戳。
LABEL-这是在 pg start backup 中指定的标签。
START TIMELINE-这是备份开始的时间线。这是为了进行健全性检查,并已在版本11中引入。
polar_non_exclusive_backup_label 文件内容示例:
postgres> cat
/home/postgres/tmp_datadir_polardb_pg_1100_bld/polar_non_exclusive_backup _label
(2)pg_stop_backup
备份完成后会发一个 stop backup 信号。一旦执行该信息以后,意味着该数据库结束了备份。
①pg_stop_backup 执行以下五个操作来完成备份:
如果 pg_start backup 强制更改了非整页写入模式,则将其重置为非整页写入模式。
写一个备份结束的 XLOG 记录。
切换 WAL 日志。(备份结束后切换日志非常重要,由于该数据库是在热备份,那么备份过程中,数据库不断发生变化,意味着备份的数据库是不一致的,将来要打开数据库时,数据库必须是一致才能打开。要将备份时不一致的数据库变为一致状态,应用在备份过程当中产生的日志,将日志应用完后就能保证该数据库是一致的。这些日志写在当前的 wal 日志中,所以切换后意味着其已经归档。如果将来该日志崩溃,恢复时可以从归档里面使用该日志的信息,这样就可以保证将数据库恢复到一致性状态,就可以打开了。)
创建备份历史文件-此文件包含备份标签文件的内容和执行 pg_stop_backup 的时间戳。
删除备份标签文件-备份标签文件是从基本备份恢复所必需的,一旦复制就不必在原始数据库群集中。
强调:数据库在一致性状态下才能够打开
(3)basebackup 备份
备份案例:
①产生压缩的 tar 包,-Ft参数指定(二进制):
pg_basebackup -D bk1 -Ft -Z 1 -P -h 127.0.0.1 (-Z 1 表示一级压缩,压缩级别越高表示压缩率越高,但是备份时间也越长)
此备份花的时间比较长,但是节省空间。支持表空间文件存放在其它目录下。
②产生跟源文件一样的格式,即原样格式,-Fp 参数指定:
pg_basebackup -D bk2 -Fp -P-h 127.0.0.1
此备份方式很快,但是不节省空间。如果有表空间路径放在其它目录下,则备份失败。
二、如何实现完全和不完全恢复
在数据库中,由于将所有对数据库的改变操作做了记录,记录这个过程就像拍电影一样,将整个剧情拍摄下来,将来如果重复想看该剧情的时候,就可以进行播放。数据库的恢复与之相同,比如,在两天前做了备份,则该备份保留的就是前两天的状态,但是数据库已经运行了两天,数据库里面发生了很多变化,现在要把数据库恢复到当前的状态,那么要用到这两天之内其数据库产生的事务的日志,该事务的日志就存放在 redo log 文件中。
一些已经用完的 redo log 会归档,所以这时会用到归档文件和当前的 redo log 文件。PolarDB 叫做 wal 文件,功能是提供一个 redo,所以也将其称为 redo log。
1.完全恢复
如果想让数据不丢失,那么恢复时就要用到从备份到当前期间产生的所有日志都要应用。这里会涉及以归档(archivelog)加当前的 wal 日志,这样才能实现完全恢复。
注意:恢复时要提供 redo ,日志从哪里取
2.介质恢复与实例恢复的区别
介质恢复:存储介质损坏,恢复原来的备份
实例恢复:实例在运行过程当中中断,这时数据库是在不一致的情况下关闭,所以下一次打开的时候需要对其进行恢复.
无论是哪种恢复,其原理相同。
(1)PITR 过程几乎与正常恢复(实例恢复)过程相同;它们之间的唯一区别是以下两点:
①从哪里读取 WAL 段/归档日志?
正常恢复模式-从 $PGDATA 目录下的 pg _wal 子目录恢复。
PITR mode-来自配置参数 archive_command 中设置的存档目录。
(介质恢复,从一个参数中告诉其归档在哪里,然后从归档日志中把日志文件 cope 到redo log 文件中进行恢复)
②从哪里读取检查点位置?
正常恢复模式-从 pg_control 文件。
PITR 模式-来自备份标签文件。
3.PITR恢复流程概述如下:
(1)为了找到重做点,PolarDB 使用内部函数read-backup-label从备份标签文件中读取“检查点位置”的值。(检测点中记录了重做点的位置)
(2) PolarDB从recovery.conf 中读取一些参数值;在本例中,restore_command 和recovery_target_time。
(restore_command 命令意思是是归档日志从哪里找,recovery_target_time 是恢复到什么位置)
(3) PolarDB 开始从重做点重放 WAL 数据,可以很容易地从“检查点位置”的值中获取。
(4) 恢复过程完成后,将在 pg_xlog 子目录(在版本10或更高版本的 pg_wal 子目录中)中创建时间线历史文件。(该历史文件很重要,是用来记录数据库什么时候做了恢复,记录数据库恢复的整个变化过程,所以该文件一定要保留,不能删除)
4.什么是时间线
所有数据库都有时间线这个概念,包括 oracle 。时间线如何理解,假设一个数据库在正常情况下,其时间线正常,但是在中间数据库可能坏了,就做了一个恢复操作,恢复到某位置后时间线不再向后走,而是重新开始一个事务的过程。这时 PolarDB 就会产生一个新的时间线,时间线就像中国的改朝换代一样,改了一个朝代以后,就有一个新的年号来替代,在数据库中用的就是时间线。
时间线是记录在 wal 日志中的,假设刚开始时数据库的实践中是1,从 wal 日志的命名可以看出其时间线。如下图,不再向后恢复,则 PolarDB 会产生一个新的时间线,同时会产生一个新的日志文件,这时,这个日志中就记录了新时间线产生的事务的日志。000000000000000A 这个符号不变,只是时间线变了,在恢复时会根据日志的名字去判断是哪个时间线产生的日志。总之,在 PolarDB 下,做了一次恢复,就会产生一条新的时间线。只要看时间线的数字就可以知道做了几次数据库的恢复。
(1)时间线历史文件
不同时间线产生的 redo log 文件名不一样,同时 PolarDB 为了进入数据块恢复整个演变的过程,会产生一个时间线历史文件。
①历史文件的命名规则如下所示:
“8-digit new timelineld" .history 比如: 00000002.history(时间线2产生的历史文件)
②时间线历史记录文件至少包含一行,每行由以下三项组成:
timelineld -用于恢复的归档日志的 timelineld。
LSN-发生 WAL 段切换的 LSN 位置
reason -人类可读的时间线为什么改变的解释。
③比如:
postgres>cat /home/postgres/archivelogs/00000002.history
1 0/A000198 before 2020-4-28 12:05:00.861324+00
注意上面1是行号,不代表特殊意义。
·假设要恢复 timelineld 为2,时间点为12:15:00,在 postgres.conf (12版本及以
后)添加如下内容:
restore_command = 'cp /mnt/server/archivedir/%f %p'
recovery
_
target_time = "2020-4-28 12:15:00 GMT"
recovery_target_timeline = 2
5. 如何执行一个完全恢复
(恢复前备份 pg_wal 目录下所有文件,pg_wal 目录必须保留)
将 PGATA 整个目录删除则 pg_wal 目录被删除,这样就不能做完全恢复了,数据被丢失,在演示时,可以删 base 的目录,该目录包含了数据库中所有的文件,这样的话就不会把 pg_wal 目录删除。在做完全恢复演示的时候,一定要保证数据库是不丢失的,恢复时数据库都存在。演示如下:
此时有三个表
Select * from t1; 运行,表里没有数据。
向里面插入数据,
insert into t1 values(1),(2),(3);
插入后 PolarDB 默认提交。
如果在操作过程中有事务没有提交,将来恢复的时候没有提交的事务会回滚,所以恢复正确的话,表t1应该有3条记录。
再向里面插入3条:
begin;
insert into t1 values(4),(5),(6);
该事务没有提交,如果数据库在这时崩溃或者坏了,将来 PolarDB 在恢复时操作应该是会回滚。
恢复正确的回滚是t1表有3条数据,要在意数据的完整性。
(1)主机断电,导致 $PGDATA/base 损坏
kill -9 'ps -eflgrep bin/postgres lgrep -v grep lawk '{print $2}’’
ps -eflgrep postgres
数据崩溃:
然后将数据库文件删除:
rm -rf $PGATA/base
注意:此时的 redo log 文件没有删除
(2)转储数据库文件
将备份的数据库恢复:
tar -zxv -f /home/postgres/bk1/base.tar.gz -C $PGDATA
(3)创建 recovery.signal
touch $PGDATA/recovery.conf
cd $PGDATA
$ vi recovery.conf
(4)修改 recovery.conf 文件,添加一下两行
restore_command = ‘cp /home/postgres/arch/%f %p’
recovery target_timeline = 'latest'
内容包括归档日志的路径和和要恢复的位置,如果要完全恢复,则在后面加’latest’,表示恢复到最后。
(5)启动数据库,自动恢复,验证数据完整性
启动数据库:$ pg_ct1 start
恢复时,没有 Oracle 那样打印非常详细的信息,PolarDB 在启动时自动恢复
$ psql testdb
Select * from t1;
恢复完后检验:有三条记录,说明是做完全恢复。
总结:$PGDATA/pg_wal 目录不能删除,否则会导致当前使用的 wal 日志文件丢失,无法完全恢复
6.不完全恢复
在 Oracle 数据库下,如果是做不完全恢复,是非常谨慎的,要执行做不完全恢复的命令,同时打开数据库时要用 resetlogs 打开,这样让操作的人明白做的是不完全恢复,其数据库会丢数据,但是 PolarDB 在这一点上没有明显的提示,所以有时会一不小心做了一次不完全恢复。比如在恢复的时候没有备份 pg_wal 目录下所有文件,不小心将日志文件删除,这时会发现本来有条件完全恢复,现在不能做完全恢复
再向里面插入3条数据,已经提交。
insert into t1 values(4),(5),(6);
(1)执行隐式不完全恢复(恢复前没有备份pg_wal目录下所有文件)
①主机断电,导致$PGDATA 目录损坏(包含pg_wal下的文件)
pg_ctl stop -m immediate #此操作不会切换当前日志
rm -rf $PGDATA/*
#前面的变量名千万别写错,否则删除的是/下面所有的文件和当前目录下所有文
件
②转储数据库文件
tar -zxv -f /home/postgres/bk1/base.tar.gz -C $PGDATA
③创建 recovery.conf
touch $PGDATA/recovery.conf
④修改 recovery.conf 文件,添加一下两行
restore_command = ‘cp /home/postgres/arch/%f %p’
recovery_target_timeline = 'latest'
⑤启动数据库,自动恢复,验证数据完整性(最后一个事务日志的数据丢失。)
前面3条的记录没有归档出来,等于原来的事务都丢了,这样的话很容易做一个隐式的不完全恢复
总结:$PGDATA/pg wal 目录如果删除,否则会导致当前使用的wal日志文件丢失,那么pg会隐式的做不完全恢复。
(2)执行时间点不完全恢复(恢复前备份 pg_wal 目录下所有文件)
时间点恢复,比如,一个误操作导致一个关键的表被删除,这时做一个时间点恢复。
假设一条时间线上有一个关键的表在某处被误删,但是这个表必须存在,这时,在某个位置有一个备份,要将这个备份恢复出来,然后应用这个时间段的归档日志或者 redo log 文件恢复到删除的这个地方之前,这样的话该表就不会被删除,但是该处往后的数据都消失,这样会丢数据,叫不完全恢复。此外注意,不能跳过该处,数据库中要求数据必须是满足数据的一致性,不能跳过该处。
①查看当前时间点
select now();
②drop 一张表:drop table t1;
再创建一张新表:create table t4 select * from t3;
然后切换日志:select pg_switch_wal()
;可多切换几个日志:
③关闭数据库,删除$PGDATA 目录下所有文件
pg_ctl stop
rm -rf $PGDATA/*
④转储数据文件
tar -vxf bk1/base.tar.gz -C $PGDATA
数据库很大的话,备份恢复的工作量非常大。给客户做恢复时,如果有1、2 T,要将备份恢复到目标数据库,可能需要7、8个小时甚至更多时间。
⑤创建 recovery.conf 文件
vi recovery.conf
⑥修改 recovery.conf 文件
restore_command = 'cp /home/postgres/arch/%f %p'
recovery _target_time=2021-12-29 04:25:21"
⑦启动数据库,执行 pg_wal replay_resume()函数,验证数据完整性(删除的表回来了,新建的没有了。)
时间点恢复后,数据默认在只读事务状态,需要执行 pg_wal_replay_resume() 使其变为读写状态。
三、PG_RMAN 备份工具(第三方)
1.pg_rman 简介
pg_rman 属于第三方的备份工具,通过它可以方便的做备份和恢复,比如,在恢复时不需要手动创建文件和向其中写内容,而是自动创建。
pg_rman 是一个独立的备份软件,类似于 oracle 的 rman,是一款操作简单功能强大的备份和恢复软件。
2.下载对应数据库版本及操作系统的 pg_rman 源码:
https://github.com/ossc-db/pg_rman/releases
下载的是 pg_rman-1.3.14-pg11.tar.gz(下载时注意数据库的版本)
源码是与操作系统无关的,在编译安装的时候会适配对应的操作系统
3.pg_rman部署
(1)上传安装包并解压安装(postgres 用户安装)
# tar vxf pg_rman-1.3.14-pg11.tar.gz
# cd pg _rman-1.3.14-pg11
# make
# make install
默认会安装到$PG_HOME/bin 目录下。
(2)初始化环境
设置备份目录,在用户配置文件里面添加如下:
export BACKUP_PATH=/home/postgres/pg_rman_bk1
让环境变量生效
source .bash_profile
初始化备份目录,验证归档路径,日志目录,同时在备份路径下产生跟目标数据库相关的文件。
$ pg_rman init
pg_rman init
iNFO:ARCLOG_PATH is set to '/home/postgres/arch'
INFO: SRVLOG_PATH is set to "
/usr/local/pg12.2/data/pg_log'
查看备份路径下的内容:
ls pg rman_bk1/
20200603 backup pg _rman.ini system_identifier timeline_history
演示如下:
4.对 pg_rman 备份
(1)pg_rman 备份
①对数据库做全备:
$ pg_rman backup --backup-mode=full -B /home/postgres/pg_rman_bk1/ -C -P#-B
参数可以省略
(还会备份归档日志,注意 PolarDB 在维护过程中日志文件要经常删除,负责归档会一直保留,用 pg_rman 备份时会全部备份,所以有些不需要归档的也会被备份,可能空间会不够,。这是一个不太智能的地方,所以需要经常去维护归档。 )
②验证数据库备份(必须要验证否则后续无法做增量备份):
$ pg _rman walidate
INFO: validate: "2020-06-0322:13:52" backup and archive log files by CRC
INFO: backup "2020-06-03 22:13:52- is valid
③查看备份信息:
$ pg_rman show
======================:=========================================
StartTime EndTime
Mode Size TLl Status
======================:=========================================
2022-04-11 01:00:55 2022-04-11 01:02:20 FULL 15MB 1 OK
将备份失败的删除:
pg_rman
delete
”
2022-04-11 01:00:55
”
在归档目录下查看现在有多少空间:
假设当前已经做了一个新的备份,以前的归档都不需要了,并且 PolarDB 的归档日志默认是1T 。因为其日志比较大,所以以前的归档比较占空间,就可以将其删除(不要将 pg_wal 目录文件删除)。
删除时可以用
pg_archivecleanup/home/postgres/arch_1 000000010000000000000017
维护,
(清除指定归档日志之前的归档日志后再做备份)
④对数据库进行增量备份(对数据库做增量备份前先做full备份)∶
$ pg_rman backup --backup-mode=incremental
备份完成后,做一个 pg_reman validate 验证:
(2) pg_rman 做不完全备份演示
对数据库做时间点不完全恢复
①查看当前的时间点
select now();
②之后 drop 一张表,再创建一张新表
Create table t4 as select * from t2;
③切换日志
select pg_switch_wal
(
);
④关闭数据库,
Pg_ct1 stop
删掉所有文件
rm-rf $PGDATA/*
⑤用 pg_rman 做时间点恢复
pg_rman restore --recovery-target-time="2021-12-29 02:41:49
自动产生一个 recovery 文件,查看:
⑥启动数据库,执行函数把只读模式改成为读写模式
select pg_wal_replay_resume();
⑦验证数据完整性,删除的表恢复回来,新建的表没有。
总结:用 pg_rman 做备份恢复,可以简化恢复的过程,使恢复变的更加方便。
(3)pg_rman 时间点恢复原理;
①在 recovery.conf 文件中自动添加恢复参数:
# added by pg_rman 1.3.9
restore_command = 'cp /home/postgres/archives/%f %p'
recovery_target_time = ‘2021-12-2902:41:49'
recovery_target_timeline = '4'
②自动生成 recovery.conf 文件,启动后自动删除
(4)pg_rman 维护
①删除备份,删除这个时间点相关联的增量备份,除了第一次增量备份和full备份:
pg_rman delete "2020-06-03 22:39:20"
②强制删除指定时间点之前的所有备份:
pg_rman delete "2020-06-0322:21;15" -f
(如果当前备份是唯一备份可能不会被删除,加-f 可以强制删除)
③清除归档,清除指定归档日志之前的归档日志:
pg_archivecleanup/home/postgres/arch_1 000000010000000000000017