数据库的备份有多种分类方式。按照备份后的文件类型,可以分为物理备份(文件系统级别的备份)和逻辑备份(备份后的文件是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文件,把系统恢复到之前的某个状态。这种备份有显著的优点:

  1. 不需要一个完美的一致的文件系统备份作为开始点。备份中的任何内部不一致性将通过日志重放来修正。
  2. 可以结合一个无穷长的WAL文件序列用于重放,可以通过简单地归档WAL文件来达到连续备份。
  3. 不需要重放WAL项一直到最后。可以在任何点停止重放,并使数据库恢复到当时的一致状态。
  4. 可以连续地将一系列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的标签、备份的起始与终止事务日志位置以及备份的起始和终止时间。返回值是备份的终止事务日志位置(同样也可以被忽略)。 在记录结束位置之后,当前事务日志插入点被自动地推进到下一个事务日志文件,这样结束的事务日志文件可以立即被归档来结束备份。