3 使用日志进行恢复的基本原理
要想恢复系统丢失的数据,做好备份工作很重要。但是,基于日志进行恢复的方式却是备份的前提。这需要了解恢复的一些基本原理。
1. 事务故障的恢复
事务故障是指事务在运行至正常终止点前被终止,这时恢复子系统应利用日志文件撤销(UNDO)此事务已对数据库进行的修改(但如果使用预写日志方式,则不需要多虑事务故障)。事务故障恢复由系统自动完成。恢复步骤:
(1) 反向扫描日志文件,查找该事务的更新操作。
(2) 对该事务的更新操作执行逆操作。即将日志文件中“更新前的值”写入数据库。这样,如果记录中是插入操作,则相当于做删除操作,若记录中是删除操作,则做插入操作;若是修改操作,则相当于用修改前值代替修改后值。
(3) 继续反向扫描日志文件,查找该事务的其他更新操作,并做同样处理。
(4) 如此处理下去,直至读到此事务的开始标记,事务故障恢复完成。
2. 系统故障的恢复
系统故障是指造成系统停止运转的任何事件,使得系统要重新启动。系统故障造成数据库不一致状态的原因有两个:一是未完成事务对数据库的更新可能已经写入数据库,二是已提交事务对数据库的更新可能还留在缓冲区没来得及写入数据库。因此恢复操作就是要撤销故障发生时未完成的事务,重做已完成的事务。这种操作就要依赖有备份好的数据、日志等信息。恢复步骤:
(1) 正向扫描日志文件,找出故障发生前已经提交的事务(这些事务既有BEGIN TRANSACTION记录,也有COMMIT记录),将其事务标识记入重做(REDO)队列。同时找出故障发生时尚未完成的事务(这些事务只有BEGIN TRANSACTION记录,无相应的COMMIT记录),将其事务标识记入撤销(UNDO)队列。
(2) 对撤销队列中的各个事务进行撤销(UNDO)处理
反向扫描日志文件,对每个UDO事务的更新操作执行逆操作。即将日志记录中“更新前的值”写入数据库。
(3) 对重做队列中的各个事务进行重做(REDO)处理
正向扫描日志文件,对每个REDO事务重新执行日志文件登记的操作。即将日志记录中“更新后的值”写入数据库。
3. 介质故障的恢复
发生介质故障后,磁盘上的物理文件和日志文件被破坏,恢复方法是重装数据库,拷贝物理备份的文件到数据库系统目录下,然后利用日志重做已完成的事务:
(1) 装入数据库后备副本,使数据库恢复到最近一次转储时的一致性状态。对于动态转储的数据库副本,还需要同时装入开始转储时刻的日志文件副本,利用恢复系统故障的方法(REDO+UNDO)才能将数据库恢复到一致性状态。PG使用WAL方式记载日志信息,所以在恢复阶段,只用了Redo的方式。
(2) 装入相应的日志文件副本(转储结束时刻的日志文件副本),重做已完成的事务。
(3) 介质故障的恢复和系统故障恢复的入口都是startupxlog(),但是介质故障的恢复要读入备份文件,设定恢复的参数,然后调用相应资源的恢复方法函数进行恢复。
(4) 即时恢复(PITR):在任何时候,PostgreSQL 都在集群的数据目录的 pg_xlog/ 子目录里维护着一套预写日志(WAL)。 这些日志记录着每一次对数据库的数据文件的修改的细节。这些日志存在是为了防止崩溃:如果系统崩溃, 数据库可以通过"重放"上次检查点以来的日志记录以恢复数据库的完整性。 但是,日志的存在让它还可以用于第三种备份数据库的策略:我们可以组合文件系统备份与 WAL 文件的备分。 如果需要恢复,我们就恢复备份,然后重放备份了的WAL文件,把备份恢复到当前的时间或指定的时间点上。之所以能这样做,是因为PG在记录日志信息的时候,记载了系统的时间信息到日志中。
(5) 时间线: 我们可以把数据库恢复到管理员(DBA)开始备份以来的任意时刻的状态。PostgreSQL 有个叫时间线的概念。 每次即时恢复到一个比 WAL 序列的结尾要早的时刻,那么就创建一个新的时间线, 以表示在该次恢复之后生成的 WAL 记录。(不过,如果恢复动作一直处理到 WAL 的结尾, 我们就不会开始一个新的时间线:我们只是扩展现有个那个。)时间线 ID 号是 WAL 段文件名的一部分, 因此新的时间线并不会覆盖以前的时间线生成的 WAL 数据。实际上我们可以归档许多不同的时间线。 虽然这些看起来像没用的特性,但它却可能常常是救命稻草。考虑一下你并不很确信应该恢复到那个时刻的情况, 这个时候你不得不做好几次试验性即时恢、复然后从中找到旧历史中最好的分支。 如果没有时间线,那么这个过程可能很快就会导致无法管理的混乱。 有了时间线,你可以恢复到任意以前的状态, 包括恢复到后来放弃的时间线分支的状态。
实际上,这里讲到的一些涉及恢复的内容,也可以应用到发生系统故障的时候,要想少丢失数据,这通常依赖与管理员的备份恢复策略和其实际需求。