[原]记一次使用flashback恢复数据,警惕自己不要浮躁,还是太嫩了

周五晚十点多,同事突然来电称操作CMS后台的时候不小心删除了很多记录(其实应该是这个CMS的逻辑问题),大概了解了情况以后,能初步判断为p_web表的2万多条数据被delete了,事发时间大概在9:30左右,这种场景几乎就是“专门”为flashback而设的了。

于是马上翻开书找到flashback那节书,先鄙视一下自己,以前所学的都还给书本了。

确认一下可以flashback的极限是多少

select 
    min(start_timestamp) 
from 
    flashback_transaction_query 
where 
    table_name='P_WEB' 
    and table_owner='CMS'

结果是前天的下午3点,比预期中的时间要长很多啊。

由于p_web表数据量不大,还不到100M,赶紧做个“备份”,免得突然犯浑把剩下的2万多条记录都整丢了,那就可以跳楼了:

create table p_web_bad
as 
select * from p_web; 

使用flashback query将9:20的数据放在一个p_web_20100507_0920表里面,让同事看看效果先:

create table p_web_20100507_0920 
as 
select 
    * 
from 
    p_web 
as of timestamp to_date('2010-05-07 21:20:00','yyyy-mm-dd hh24:mi:ss');

其实我也是笨,脱离了表现层的数据能看出什么呢?除了“感觉”上是正确的一点说服力也没有。

同事说这个数据没问题。为了进一步确定事发的准确时间点,使用flashback transaction query 将自8:30以来的所有undo操作列出,这样可以找出什么时候开始删除的了。

create table p_web_undo_20100507_2030 
as
select 
    start_timestamp,operation,undo_sql 
from 
    flashback_transaction_query 
where 
    table_name='P_WEB' 
    and table_owner='CMS' 
    and start_timestamp>to_date('2010-05-07 20:30:00','yyyy-mm-dd hh24:mi:ss')
order by 
    start_timestamp desc 

经过一个简单的查询从start_timestamp 能查出是21:39:04开始删除的,只要将flashback到21:38:00问题就OK了啦。

flashback table p_web 
    to timestamp to_date('2010-05-07 21:38:00','yyyy-mm-dd hh24:mi:ss');

此时,报了一个错:

ORA-08189: cannot flashback the table because row movement is not enabled

小问题,enable row movement 就是了

alter table p_web enable row movement;

我再闪(flashback):

flashback table p_web 
    to timestamp to_date('2010-05-07 21:38:00','yyyy-mm-dd hh24:mi:ss');

shit!竟然出现了flashback的克星:

ora-01555 snapshot too old

明明前面查到可以flashback的时间很长的啊,莫非是前面几个大create table把undo内容刷走了,这也太快了吧@_@!

先不管了,我同事还在着急的等待,虽然没有催我,但是“弄丢”数据的心情我很清楚。

幸好前面创建了一个p_web_undo_20100507_2030表,里面有一对undo的sql,可以将被delete的数据再insert回去。

于是写了个简单的过程做这件事:

declare
    v_undo_sql varchar2(4000);
    CURSOR cur is select substr(undo_sql,1,length(undo_sql)-1) --将undo_sql最后那个分号去掉
    from p_web_undo_20100507_2030 where operation='DELETE'; 
    BEGIN 
    open cur;
    loop
        fetch cur into v_undo_sql;
        EXIT when cur%NOTFOUND; 
        execute immediate v_undo_sql;
        --dbms_output.put_line(v_undo_sql);
    END LOOP;
    CLOSE cur;
END delete_state ;

经过“漫长”的等待,终于将误删的数据恢复回来了。

 

经过这次数据恢复让我认识到:

1。虽然掌握了基本概念,但是熟练程度还不够,体现在临急翻书上。

2。实际操作经验欠缺,整个过程实际上没多少东西,但是却足足折腾了一个半小时。

3。前瞻性不足,这个数据库是我维护的,对于误操作删除数据这种问题应该要有所觉悟,就这个问题而言,我做的措施仅仅是扩大了undo表空间。

总的来说,就是太嫩了。

posted @ 2010-05-08 22:48  killkill  阅读(2438)  评论(1编辑  收藏  举报