数据库的备份有多种分类方式。按照备份后的文件类型,可以分为物理备份(文件系统级别的备份)和逻辑备份(备份后的文件是sql文件或特定格式的导出文件);按照备份过程中是否停止数据库服务,可分为冷备份(备份过程中停止数据库服务)和热备份(备份过程中数据库服务开启并可供用户访问);按照备份是否是完整的数据库,可分为全量备份(备份是完整的数据库)和增量备份(备份是上一次全量备份后数据库改变的内容)。
Postgresql的常见备份方式有以下三种:
1. 文件系统级别的冷备份。
这种备份方式需要关闭数据库,然后拷贝数据文件的完整目录。恢复数据库时,只需将数据目录复制到原来的位置。该方式实际工作中很少使用。
2. SQL转储。
这里我们用到的工具是pg_dump和pg_dumpall。
这种方式可以在数据库正在使用的时候进行完整一致的备份,并不阻塞其它用户对数据库的访问。它会产生一个脚本文件,里面包含备份开始时,已创建的各种数据库对象的SQL语句和每个表中的数据。可以使用数据库提供的工具pg_dumpall和pg_dump来进行备份。pg_dump只备份数据库集群中的某个数据库的数据,它不会导出角色和表空间相关的信息,因为这些信息是整个数据库集群共用的,不属于某个单独的数据库。pg_dumpall,对集簇中的每个数据库调用pg_dump来完成该工作,还会还转储对所有数据库公用的全局对象(pg_dump不保存这些对象)。 目前这包括适数据库用户和组、表空间以及适合所有数据库的访问权限等属性。
例如,在我的计算机上,可使用如下命令对名为dbname的数据库进行备份:
pg_dump –h 127.0.0.1 -p 5432 -U postgres -c -C –f dbname.sql dbname
使用如下命令可对全部pg数据库进行备份。
pg_dumpall –h 127.0.0.1 –p 5432 -U postgres –c -C –f db_bak.sql
恢复方式很简单。执行恢复命令即可:
psql –h 127.0.0.1 -p 5432 -U postgres –f db_bak.sql
3. 连续归档
这种方式的策略是把一个文件系统级别的全量备份和WAL(预写式日志)级别的增量备份结合起来。当需要恢复时,我们先恢复文件系统级别的备份,然后重放备份的WAL文件,把系统恢复到之前的某个状态。这种备份有显著的优点:
- 不需要一个完美的一致的文件系统备份作为开始点。备份中的任何内部不一致性将通过日志重放来修正。
- 可以结合一个无穷长的WAL文件序列用于重放,可以通过简单地归档WAL文件来达到连续备份。
- 不需要重放WAL项一直到最后。可以在任何点停止重放,并使数据库恢复到当时的一致状态。
- 可以连续地将一系列WAL文件输送给另一台已经载入了相同基础备份文件的机器,得到一个实时的热备份系统。
如何进行连续归档呢?
下面的实例中,操作系统为windows 10,Postgresql的版本为9.6。
首先,需要修改postgresql.conf文件的几个参数修改如下:
wal_level = ‘replica’
archive_mode = ‘on’
archive_command = 'copy /y "%p" "D:\\archive\\%f"'
archive_command执行时,%p会被要被归档的文件路径所替代,而%f只会被文件名所替代。如果你需要在命令中嵌入一个真正的%字符,可以使用%%。 “D:\\archive\\”替换为归档日志的存放路径,要确保归档的目录是存在的。
之后需要重启数据库使配置生效。
接下来需要制作一个非排他的基础备份。Postgresql提供了排他备份和非排他备份两种备份方式,推荐使用非排他的备份方式。
1. 作为一个具有运行 pg_start_backup 权利的用户连接到服务器(不在乎是哪个数据库)并且发出命令:
Select pg_start_backup('backup_label', false, false);
2. 对数据库进行一次文件系统级别的备份。即将postgresql的安装目录下的data目录及其内容复制到其他位置。
3. 在同一个连接中,发出命令:
select * from pg_stop_backup(false);
这个命令代表结束一次非排他的备份。
现在来看基于时间点的恢复。
假如你的数据库出现了故障,需要恢复到之前的某个时刻的一致的状态,就需要进行基于时间点的恢复。
其过程是:
1. 如果服务器仍在运行,停止它。
2. 如果你具有足够的空间,将整个集簇数据目录和表空间复制到一个临时位置。注意最好是拷贝而不是移动。如果你没有足够的空间,你至少要保存集簇的pg_xlog子目录的内容,因为它可能包含在系统垮掉之前还未被归档的日志。
3. 移除data 目录及其所有子文件和子目录。
4. 从文件系统备份中恢复数据库文件。注意它们要使用正确的用户恢复并且使用正确的权限。如果你在使用表空间,你应该验证pg_tblspc/中的符号链接被正确地恢复。
5.现在已经从备份中恢复了整个数据目录,接下来,你需要部分或全部删除数据目录中的下列文件,如果它们存在:
(1) postmaster.pid;(必须)
(2) pg_xlog中的文件;(必须)
(3) pgsql_tmp开头的临时文件;(可选)
(4) postgresql.auto.conf.tmp;(可选)
(5) pg_replslot目录中的文件;(可选)
(6) pg_stat_tmp目录中的文件。(可选)
6. 如果你有在第2步中保存的未归档WAL段文件,把它们拷贝到pg_xlog/中或WAL日志的归档目录中。
在集簇数据目录中创建一个恢复命令文件recovery.conf。如果你想恢复到最近的一致状态,在recovery.conf写入如下两行:
restore_command = 'copy /y D:\\archive \\%f\\%p'
recovery_target_timeline = 'latest'
其中,restore_command的内容表示将归档日志文件夹中的内容拷贝到pg_xlog,其参数的含义与上文archive_command的参数含义完全相同;recovery_target_timeline = 'latest' 表示恢复到最近的时间点。
7. 如果要阻止普通用户在成功恢复之前连接,请修改pg_hba.conf。
8. 启动服务器。服务器将会进入到恢复模式并且进而根据需要读取归档WAL文件。恢复可能因为一个外部错误而被终止,可以简单地重新启动服务器,这样它将继续恢复。恢复过程结束后,服务器将把recovery.conf重命名为recovery.done(为了阻止以后意外地重新进入恢复模式),并且开始正常数据库操作。
9. 检查数据库的内容来确保你已经恢复到了期望的状态。如果没有,返回到第1步。如果一切正常,通过恢复pg_hba.conf为正常来允许用户连接。
这样就完成了一次文件系统级别的全量备份,并实现了WAL文件级别的增量备份。
附:pg_start_backup() 和 pg_stop_backup()做了什么?
1. pg_start_backup()
pg_start_backup() 的函数原型如下:
pg_start_backup(label text [, fast boolean [, exclusive boolean ]])
其中label是用来唯一标识这次备份操作的任意字符串,fast 表示是否立即执行强制的检查点,exclusive 表示该备份是否是一个排他备份。
使用该函数时,推荐将exclusive设置为false,即非排他方式备份。
执行下面的命令:
Select pg_start_backup('backup_label', false, false);
这条命令会产生三个动作:
1. 在缓冲区中创建两个变量:label_file和tblspc_map_file。前者包含WAL的起始位置、检查点的起始位置、备份方法、备份模式、备份开始时间和备份标签的名称(本例中,名称为“backup_label”)等信息;后者包含 “pg_tblspc/”中表空间符号链接的信息,如果它们存在。
2. 强制发生一次检查点。将检查点开始前提交的事务对数据库的修改刷新到磁盘。
3. 置写日志标志为:XLogCtl->Insert.forcePageWrites = true。把这个标志设置为true后,如果在备份期间时有其他事务修改数据库,那么系统会把被修改的数据页在修改前的完整页面都记录到WAL中,而不仅仅是记录页面中的变化的部分。
为什么要将完整的页面记录到WAL中呢?
原因是在备份过程中,其他事务修改数据库,可能会造成备份后的数据存在新旧数据混合的情况,甚至同一数据页面也会出现这种情况。假如WAL日志中仅仅记录变化的那部分的内容,就无法将数据库恢复到上次备份结束时刻的状态。所以要将修改后的完整页面写入WAL中,以保证数据库恢复后的一致性。
2. pg_stop_backup()
pg_stop_backup()的函数原型如下:
pg_stop_backup([, exclusive boolean ]);
exclusive 表示该备份是否是一个排他备份。
如果采用非排他方式备份,执行:
Select pg_stop_backup(false);
这条命令会产生如下动作:
1. label_file和tblspc_map_file的内容会包含在该函数返回的结果中,并且应该被写入到该备份的一些文件中(这些内容不在数据目录中)。
2. 在事务日志归档区里创建一个备份历史文件(.hostory)。这个历史文件包含 pg_start_backup的标签、备份的起始与终止事务日志位置以及备份的起始和终止时间。返回值是备份的终止事务日志位置(同样也可以被忽略)。 在记录结束位置之后,当前事务日志插入点被自动地推进到下一个事务日志文件,这样结束的事务日志文件可以立即被归档来结束备份。
参考文献
[1] PostgreSQL全球开发小组. PostgreSQL 9.6.0 文档. 连续归档和时间点恢复(PITR).
[2] 2ndquadrant. What does pg_start_backup() do?. 2017-1-23
[3] Michael Paquire. Postgres 9.6 feature highlight - Non-exclusive base backups. 2016-5-31