Oracle Flash Back -------闪回到一个合适的时间

使用一条简单的SQL语句,把表或者数据库恢复到过去的某个时间点。

 

新年的圣诞夜前夕,顶点银行的DBA约翰正在和他的朋友们一起狂欢,为新年的到来倒计时。正当午夜的时钟敲响,大家一起欢呼时,他的手机忽然响了起来。银行数据中心里,年终计帐程序正忙于计算利息,但是发生了一些问题,所有的利息都被计算错了。幸运的是,开发团队找到了问题所在并且开发了一个应急方案,但是这个方案不能撤消已经造成的损害。运算中心的经理问约翰是否能以某种方式把数据库恢复到进行利息计算之前的状态,那时的时间大概是11:00pm。

 

对于您这位DBA来说,听上去是不是很熟悉?约翰应该怎么办呢?

 

在Oracle 10g之前,约翰所能做的是建立一个时间点恢复,把数据库恢复到一个期望的时点上。不幸的是,银行的每日备份程序在那时还没有运行,因此他需要使用差不多24小时的归档日志来恢复数据库。

 

在Oracle 9i中,有另外一个选择,可以利用闪回查询特性把表中的记录恢复到11:00pm,然后再手工建立一组表。这个方法尽管可能,但是如果表的数量太多的话是不现实的。

 

Flashabck Table

 

我们看一下上面的情况,出现问题的利息计算程序可能只影响了很少的几个表。比如说,它只是更新了Accounts表中的余额。如果是这样,约翰可以利用flashback table特性把表恢复到过去的某个时点。

 

为了执行flashback table操作,没有什么特别的设置需要做。唯一的要求是打开表的row movement特性,可以在表创建的时候或者利用 ALTER TABLE ACCOUNTS ENABLE ROW MOVEMENT 来完成。FLASHBACK TABLE命令会从回滚段中读取表过去的镜像并利用Oracle 9i中引入的闪回查询技术重新构建表中的记录。

 

如果非DBA或者非模式的拥有者需要执行FLASHBACK TABLE操作,她需要有该表的SELECT, DELETE, INSERT, ALTERFLASHBACK权限,或者具有等价的ANY TABLE系统权限。

对于约翰来说,ACCOUNTS表如下:

ACCOUNT_NO NUMBER(12), BALANCE NUMBER(15,2) STATUS CHAR(1)  STATUS列的值通常情况下是"A"(active),但是当利息计算程序启动时,所有的账户都被冻结,STATUS字段被更新成"F"(frozen)。当该账户的利息计算完成时,STATUS标志被更新成"I"(interest applied)。

 

下面是约翰为了使用FLASHBACK TABLE特性所遵循的步骤:

  1. 他询问需要把数据库恢复到得大概时点,答案是大约11:00pm。
  2. 他定义了需要恢复到的逻辑状态。下面是现在查询该表时得到的结果。select status, count(*) from ACCOUNTS group by status; STATUS COUNT(*) ------ -------- I 27088 F 19999
  3. 结果显示迄今为止已经有27088个账户被处理过(status=I)。他需要恢复到的逻辑状态是所有的的账户的状态为"F",在这之前的状态是"A"。他必须把所有的账户状态恢复到"F",大概在11:00p.m。
  4. 他查看了一个状态为"I"的现有账户,并且把它作为将来测试时的验证基准。select account_no, balance from ACCOUNTS where status = 'I' and rownum < 2; ACCOUNT_NO BALANCE ----------- -------- 21633 3913.49
  5. 他执行了下面的命令把ACCOUNTS表恢复到11:00p.m时的状态:flashback table ACCOUNTS to timestamp to_timestamp ('12/31/2003 23:00:00','mm/dd/yyyy hh24:mi:ss');  哇!整个表被恢复到了指定的时间戳,约翰可以把它恢复到回滚段允许的任意远的时间点。除了使用时间戳,还可以使用系统修改号(SCN),如下:flashback table ACCOUNTS to SCN 9988653338;   表被恢复到11:00p.m,但是它是不是我们期望的点呢?约翰再次检查了账户的状态:select status, count(*) from ACCOUNTS group by status; STATUS COUNT(*) ------ -------- I 88 F 46999
  6. 仍然有88条记录的状态为"I",所以11:00p.m还不够早;约翰还需要把表恢复到更早的时间。他把表闪回到10:30p.m然后再次查看账号状态:        flashback table ACCOUNTS to timestamp to_timestamp ('12/31/2003 22:30:00','mm/dd/yyyy hh24:mi:ss'); select status, count(*) from ACCOUNTS group by status; STATUS COUNT(*) ------ -------- A 47087          
  7.  约翰闪回的太远了,所有的账号状态都是"A",在"F"之前的状态。因此,应该选择更近一点的时间10:45p.m。flashback table ACCOUNTS to timestamp to_timestamp ('12/31/2003 22:45:00','mm/dd/yyyy hh24:mi:ss'); select status, count(*) from ACCOUNTS group by status; STATUS COUNT(*) ------ -------- F 47087
  8. 现在的结果正是我们想要的,他查询了之前作为基准账户的信息:select balance from ACCOUNTS where account_no = 21633; BALANCE ------- 3836.75
  9. 这个账户之前的余额是$3,913.49,现在时10:45p.m的状态。操作完成了,flashback命令可以向前或向后闪回到某个精确地时间点。

因为表从来没有被drop掉,所有依赖它的对象-----例如索引、约束、触发器等都是完好可用的。所有引用了该表的独立对象,例如存储过程也是有效地。

如果除了ACCOUNTS表外,约翰还想闪回TXN表,那么他可以利用逗号来分隔这些表,从而一次闪回多个表:

flashback table BANK.ACCOUNTS, bank.txn to scn 1234567;

整个表的闪回操作通过一条强大的SQL语句来完成。

 

我们看一下另外一种情况,假如劳拉不小心drop了一个关键的查找表GL_MASTER。发现错误后,她问约翰可不可以恢复这张表。在Oracle10g中,drop一张表时只是重命名了这个表并且把它放入逻辑容器回收站中。

 

为了恢复这个表,约翰执行了下面的命令:

flashback table gl_master to before drop; 不需要任何恢复工作,这张表立刻就重现出现了。注意,不像之前的闪回操作,这个操作不需要从回滚段中重建数据,只是把表从回收站中移了回来。

 

Flashback Database

当逻辑错误不止发生在几个表上时,直接使用Flashback Database命令可以把数据库更快的恢复到之前的时间点。Flashback Database命令不是使用回滚段,而是使用磁盘上的一个被称为闪回恢复区来恢复数据库。利用Flashback Database命令,不用使用数据库备份就可以恢复数据库。

 

为了闪回整个数据库,约翰需要做下面的准备工作,从而使数据库具有可被闪回的能力:

  1. 通过设置下面的参数配置一个2G大小的闪回恢复区:db_recovery_file_dest = /usr/users/oracle/10.1/recovery_area db_recovery_file_dest_size = 2G
  2. 使用下面的参数配置一个以分钟为单位的最大闪回时间:db_flashback_retention_target = 1440 这个值代表了最大的可闪回的时间,最大的可能时间由闪回恢复区的可用空间决定。以上的三个参数都可以通过ALTER SYSTEM命令设置。
  3. 使数据库可闪回,数据库必须工作在archivelog模式。在MOUNT阶段,OPEN之前,约翰执行了下面的命令:alter database flashback on; 当允许FLASHBACK DATABASE的时候,它将定期的把对数据块的修改写到一个特定类型的日志中,这个日志叫闪回日志。这些日志不是由传统的日志写入器(LGWR)进程写入的,而是由新的叫做恢复写入器的进程(RVWR)负责。不像常规的重做日志,闪回日志文件不需要DBA来创建和维护,它们由Oracle Managed Files(OMF)自动在指定的闪回恢复区中创建,他们不会被归档。

 

Flashback操作

  1. 在上面所描述的顶点银行的例子中,约翰确定闪回表是不可行的,他需要把整个数据库回滚到过去的某个时点。他选择了11:00p.m作为起点并执行了下面的命令:flashback database to timestamp to_timestamp ('12/31/2003 23:00:00','mm/dd/yyyy hh24:mi:ss'); 这个操作执行了所有需要的工作,比如利用闪回恢复区的信息恢复数据文件。整个数据库被恢复到了11:00p.m的状态,约翰不需要执行任何手工恢复操作。除了使用时间戳,还可以使用SCN来闪回。在警告日志中会记录所执行的闪回操作。
  2. 尽管数据库被恢复到了11:00p.m,问题是,它是不是正确的时间呢?约翰使用了和先前一样的参考点技术。他以只读的方式打开数据库并查询账户的状态:alter database open read only; select status, count(*) from BANK.ACCOUNTS group by status; STATUS COUNT(*) ------ -------- I 255 F 46832
  3. 答案是否定的,仍然有255条记录的状态为"I",他需要闪回到更早的时间,比如10:30p.m.,如下:shutdown immediate startup mount flashback database to timestamp to_timestamp ('12/31/2003 22:30:00, 'mm/dd/yyyy hh24:mi:ss'); alter database open read only;
  4. 在ACCOUNTS表上执行同样的查询:select status, count(*) from BANK.ACCOUNTS group by status; 他发现所有的账户状态都是"A",意思是他闪回的太远啦,把数据库闪回到更近的状态,比如10:45p.m.,如下:shutdown immediate startup mount flashback database to timestamp to_timestamp ('12/31/2003 22:45:00, 'mm/dd/yyyy hh24:mi:ss'); alter database open read only;
  5. 现在再次查询状态:                                                    select status, count(*) from bank.accounts group by status; 所有的账户状态都是"F",正是我们需要的。现在,约翰把数据库恢复到了他需要的状态,他可以打开数据库并执行所需的更新了:shutdown immediate startup mount alter database open resetlogs;

你可能感兴趣的:(database)