7天突破PolarDB for PostgresSQL
第五讲 备份与恢复实践
分享人:陈卫星,CUUG 高级数据库专职讲师、PostgreSQL 中文社区培训委员会委员 PalarDB社区培训委员会主委
视频地址:
https://developer.aliyun.com/learning/course/992/detail/14979
目录:
一、Base Backup简介
二、数据库即时恢复(PITR)
三、时间线TimelineId和时间线历史文件
四、数据库完全恢复
五、pg_rman工具
一、Base Backup简介
数据库在运行中的备份方式称为热备份(Base Backup),其标准过程如下:
- 调用pg_start_backup函数;
- 建立快照;
- 调用pg_stop_backup函数,停止备份。
1、pg_start_backup
pg_start_backup执行以下四个操作:
a. Force into the full-page write mode;
- 将数据库变成强制的full-page写入模式,full-page写入是将数据块内容写入redo log文件中,即pg的WAL文件;
- 通常数据块内容是写入数据文件而非日志文件,日志文件记录的是事务性信息,但在备份时为了避免块不一致,需要将数据块写入WAL文件;
b. Switch to the current WAL segment file(version 8.4 or later);
- 将当前的WAL日志进行切换,以记录新事务;
c. Do checkpoint;
- 做一个检查点,记录当前备份的位置,作为之后数据库恢复的起始位置;
d. Create a backup_label file;
- 同时会生成一个backup_label文件,此文件创建于base目录的同一层,包含有关基本备份本身的基本信息,例如此检查点位置。
- backup_label文件包含如下内容:
- CHECKPOINT LOCATION:记录此命令创建的检查点的LSN位置;
- STARTWAL 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:备份开始的时间线,这是为了进行健全性检查,并已在PolarDB版本11中引入。
- backup_label文件示例:
polar_non_exclusive_backup label文件内容示例:
postgres>cat /home/postgres/tmp_datadir_polardb_pg_1100_bld/polar_non_exclusive_backup_label
START WAL LOCATION:0/9000028(file000000010000000000000009)
CHECKPOINT LOCATION:0/9000060
BACKUP METHOD: pg_start_backup
BACKUP FROM: master
START TIME: 2020-4-28 11:45:19GMT
LABEL: Weekly Backup
2、pg_stop_backup
pg_stop_backup执行以下五个操作来完成备份:
a.如果pg_start_backup强制更改了非整页写入模式,则将其重置为非整页写入模式;
b.写一个备份结束的XLOG记录;
c.切换WAL日志:如前所述,数据库需要在一致性状态下才能打开,然而在热备份过程中,数据库会不断发生变化,需要应用在备份中生成的日志来确保数据库的一致性,这些日志存放在WAL日志中,切换后已提交的WAL日志归档,以便在数据库恢复时使用;
d.创建备份历史文件:此文件包含备份标签文件的内容和执行pg_stop_backup的时间戳;
e.删除备份标签文件:备份标签文件是从基本备份恢复所必需的,一旦复制,就不必在原始数据库群集中。
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
此备份方式很快,但是不节省空间。如果有表空间路径放在其它目录下,则备份失败。
二、数据库即时恢复(PITR)
- 什么是PITR?
PITR(Point-in-Time Recovery)指使用之前的备份,恢复到指定时间点的恢复方式,PITR恢复依赖于WAL数据和Archive logs文件,来实现完全恢复。
- PITR与正常恢复(实例恢复)的区别
实例恢复指当实例在运行中突然中断,数据库在不一致的情况下关闭、重启数据库时需要进行的恢复。
PITR过程与正常恢复(实例恢复)过程几乎相同,它们之间的区别有以下两点:
a. 从哪里读取WAL段/归档日志?
- 正常恢复模式:从$PGDATA目录下的pq_wal子目录;
- PITR 模式:来自配置参数archive_command中设置的存档目录;
b.从哪里读取检查点位置?
- 正常恢复模式:从pq_control文件;
- PITR模式:来自备份标签文件。
3.PITR恢复流程
a. 为了找到重做点,PolarDB使用内部函数read-backup-label从备份标签文件中读取“检查点位置”(checkpoint)的值;
b. PolarDB从recovery.conf中读取一些参数值;在本例中,restore_command(归档日志的位置)和 recovery_target_time(恢复到什么时间点);
c. PolarDB开始从重做点重放WAL数据,可以很容易地从“检查点位置”的值中获取;
d. 恢复过程完成后,将在pg_xloq子目录(在版本10或更高版本的pq_wal子目录中)中创建时间线历史文件,用来记录数据库恢复的过程。
三、时间线TimelineId和时间线历史文件
- TimelineId
如上图所示,在PolarDB中,数据库正常运行会产生一条时间线(timelineId=1),如果数据库在运行中出现问题,恢复重启后就会形成新的时间线(timelineId=2)。
时间线被记录在WAL日志中,日志内容基于时间线命名,以区别恢复前后的日志,通过时间线的数字,便可以推断数据库经历了几次恢复操作。
- 时间线历史文件
时间线历史文件的命名规则,是由8位数字组成新的timelineId.history,如:
00000002.history
时间线历史记录文件至少包含一行,每行由以下三项组成:
- timelineId:用于恢复的归档日志的timelineId;
- LSN:发生WAL段切换的LSN位置;
- Reason:人类可读的时间线为什么改变的解释;
时间线示例:
postgres>cat/home/postgres/archivelogs/00000002.history
1 0/A000198 before 2020-4-28 12:05:00.861324+00
注:上面1是行号,不代表特殊意义。
- 使用时间线历史文件进行PITR恢复
使用时间线历史文件进行PITR恢复需要三个参数:从哪里恢复归档数据、要恢复的时间点、以及timelineID。
假设要恢复timelineId为2,时间点为12:15:00,在postgres.conf(12版本及以后,之前版本是recovery.conf)中添加如下内容:
restore_command=‘cp /mnt/server/archivedir/%f %p’
recovery_target_time = "2020-4-28 12:15:00 GMT”
recovery_target_timeline = 2
四、数据库完全恢复
如前所述,完全恢复依赖于Archive log + WAL日志。
Achive log:从数据库备份到恢复中间产生的数据库日志文件,属于已归档文件;
WAL日志:当前数据库日志文件。
1.执行完全恢复(恢复前备份pg_wal目录下所有文件)
a.主机断电,导致$PGDATA/base损坏;
kill -9`ps-ef∣grep bin/postgres∣grep -v grep∣awk‘{print $2}'`
rm –rf $PGDATA/base
b. 转储数据库文件;
tar –zxv –f /home/postgres/bk1/base.tar.gz –C $PGDATA
c.创建recovery.signal;
touch $PGDATA/recovery.conf
d.修改recovery.conf文件,添加以下两行:
restore_command=‘cp/home/postgres/arch/%f %p’
recovery_target_timeline=‘latest’
latest:表示需要完全恢复,恢复目标时间线位置为最后;
e.启动数据库,自动恢复,验证数据完整性;
pg_ctl start
总结:
$PGDATA/pq_wal目录不能删除,否则会导致当前使用的wal日志文件丢失,无法完全恢复。
2.执行隐式不完全恢复(恢复前没有备份pg_wal目录下所有文件)
a.主机断电,导致$PGDATA目录损坏(包含pg_wal下的文件)
pg_ctl stop –m immediate #此操作不会切换当前日志
rm –rf $PGDATA/* #前面的变量名不能写错,否则删除的是/下面所有的文件和当前目录下所有文件;
b.转储数据库文件
tar –zxv –f /home/postgres/bk1/basetargz –C $PGDATA
c.创建recovery.conf
touch $PGDATA/recovery.conf
d.修改recovery.conf文件,添加以下两行:
restorecommand=‘cp/home/postgres/arch/%f %p’
recovery target timeline=‘latest’
e.启动数据库,自动恢复;
f.验证数据完整性:最后一个事务日志的数据丢失。
pg_ctl start
总结:
$PGDATA/pq_wal目录如果删除,会导致当前使用的wal日志文件丢失,那么pg会隐式的做不完全恢复。
3.执行时间点不完全恢复(恢复前备份pg_wal目录下所有文件)
由于误操作导致一个关键的表被删除,这种情况下需要进行时间点恢复,即从数据库备份的时间点到删除表的时间点,恢复这段时间所有已提交并存档的日志,然而在删除表时间点后的日志将会丢失,因此称为时间点不完全恢复。
a.查看当前时间点;
select now();
2022-04-11 00:35:55,记下这个时间点。
b. drop一张表,再创建一张新表;
drop table t1;
creat table t4 as select * from t3;
c.切换日志;
select pg_switch_wal();
d.关闭数据库,删除$PGDATA目录下所有文件;
pg_ctl stop
rm –rf $ PGDATA/*
e.转储数据文件;
tar –vxf bk1/basetargz –C $PGDATA
f.创建recovery.conf文件;
g,修改recovery.conf文件;
restore_command =‘cp/home/postgres/arch/%f %p’
recovery_target_time=‘2022-04-11 00:35:55’
h.启动数据库,执行pg_wal_replay_resume()函数(将只读模式改为读写模式);
i.验证数据完整性:上述操作删除的表回来了,新建的表没有。
五、pg_rman工具
1.pg_rman简介
pg_rman是一个独立的备份软件,类似于oracle的rman,是一款操作简单、功能强大的备份和恢复软件。
下载对应数据库版本及操作系统的pgrman源码:
https://github.com/ossc-db/pg_rman/releases
下载pg_rman-1.3.14-pg11tar.gz(根据当前使用的PolarDB版本选择下载)。
源码与操作系统无关,在编译安装的时候会适配对应的操作系统。
2.pg_rman部署
a. 上传安装包并解压安装(postgres用户安装)
tar vxf pg_rman-1.3.14-pg11.tar.gz
cd pg_rman-1.3.14-pg11
make
make install
默认会安装到$PG_HOME/bin目录下。
b.初始化环境
- 设置备份目录,在用户配置文件里面添加如下:
export BACKUP_PATH=/home/postgres/pg_rman_bk1
- 让环境变量生效
source.bash_profile
- 初始化备份目录,验证归档路径、日志目录,同时在备份路径下产生跟目标数据库相关的文件;
pg_rman init
INFO:ARCLOG PATH is set to‘/home/postares/arch’
INFO:SRVLOG PATH is set to‘/usr/local/pg12.2/data/pg_log’
- 查看备份路径下的内容:
ls pg_rman_bk1/
3.pg_rman备份
a.对数据库做全备:
pg_rman backup --backup-mode=full –B /home/postgres/pgr_manbk1/ -C –P #-B参数可以省略
注:该命令会把全部数据进行备份,包括归档日志,如果不及时清理归档日志,全备可能会因为存储空间不足而备份失败。而PolarDB默认的归档日志为1G,因此应及时将旧的归档文件删除,详见后面pg_rman维护部分。
b.验证数据库备份(必须要验证,否则后续无法做增量备份):
pg_rman validate
c.查看备份信息:
pg_rman show
注:因磁盘空间不足导致的备份失败,也会形成备份文件,应及时查看并删除。
d.对数据库进行增量备份(对数据库做增量备份前先做full备份):
pg_rman backup –backup-mode=incremental
4.pg_rman恢复
a.对数据库做完全恢复
- 主机断电,实例崩溃,导致$PGDATA/base目录损坏;
kill -9`ps -ef∣grep bin/postgres∣grep -v grep∣awk‘{print $2}'`
rm -rf $PGDATA/base
- 转储数据库文件
pg_rman restore
- 启动数据库
pg_ctl start
- 验证数据完整性
注意:$PGDATA/pg_wal下的日志文件不能丢失,否则要做不完全恢复。
b.对数据库做隐式不完全恢复
- 主机断电,实例崩溃,导致$PGDATA/下的所有文件损坏;
kill -9`ps -ef∣grep bin/postgres∣grep -v grep∣awk‘{print $2}'`
rm -rf $PGDATA/*
- 转储数据库文件
pg_rman restore
- 启动数据库
pg_ctl start
- 验证数据完整性:当前正在使用的wal段文件包含的事务数据丢失。
c.对数据库做时间点不完全恢复
- 查看当前时间点;
select now();
- drop一张表,再创建一张新表;
drop table t1;
creat table t4 as select * from t3
- 切换日志;
select pg_switch_wal();
- 关闭数据库;
pg_ctl stop
- 用pg_rman做时间点恢复
pg_rman restore –recovery-target-time=“2022-04-11 02:41:49”
同时会自动生成一个recovery.conf文件。
- 启动数据库,执行函数,把只读模式改成读写模式;
select pg_wal_replay_resume();
- 验证数据完整性:删除的表恢复回来,新建的没有。
d. pg_rman时间点恢复原理:
- 在recovery.conf文件中自动添加恢复参数:
# added by pg_rman 1.3.9
restore_command =‘cp /home/postgres/archives/%f %p’
recovery_target_time =‘2021-12-29 02:41:49'
recovery_target_timeline =‘4'
- 自动生成recovery.conf文件,启动后自动删除。
5.pg_rman维护
a.删除备份,删除这个时间点相关联的增量备份,除了第一次增量备份和full备份:
pg_rman delete“2020-06-03 22:39:20”
b.强制删除指定时间点之前的所有备份:
pg_rman delete“2020-06-03 22:21:15”-f
c.清除归档,清除指定归档日志之前的归档日志:
pg_archivecleanup /home/postgres/arch_1 000000010000000000000017
注:在清除文件时不要误删除.history文件。