晚上一个客户联系我,开发修改数据,误删除了一批数据(一千条左右),希望我能帮忙进行恢复.
首先询问客户是使用了什么方法来删除数据的,不同的删除方法恢复的难易程度不同.
1.如果是delete,如果时间不长,可以通过闪回查询的方法将数据查询出来.
2.如果是delete,时间较长,如果归档日志还存在,那么可以使用logmnr的方式.
3.如果是truncate,那么只有进行备份恢复了.
这里客户使用的是delete的方式,大概半小时前执行的操作.由于是晚上操作很少,那么尝试使用闪回查询:
select *
from comm.ss_dict as of timestamp to_timestamp('2020-04-07 21:15:00', 'yyyy-mm-dd hh24:mi:ss')
;
但是这里直接报错了,报错的意思是闪回查询的时间点此表做了ddl操作.询问客户,确实删除完数据之后客户做了修改表结构的操作.
那么这里就只能使用logmnr了
客户环境是一个RAC环境,幸运的时候客户执行的plsql dev界面还在,通过界面我们查询到连接的会话是在节点2上,这样就不需要分析节点1的归档日志了.
logmnr的步骤大致分为如下:
-
确定你要分析的归档日志序号
可以使用如下sql查询节点2的归档时间:
select * from ( select name,thread#,sequence#,first_time from v$archived_log where thread#=2 order by first_time desc) where rownum<101 ;
例如这里确定的sequence号为1000-1010
-
指定挖掘队列
可以使用如下的sql来添加归档日志
select 'EXECUTE DBMS_LOGMNR.ADD_LOGFILE('''||name||''');' from v$archived_log where (thread#=2 and sequence# between 1000 and 1010 and name is not null);
将上面得到的sql,在sqlplus中进行执行.
-
开始挖掘
开始挖掘,由于这里没有数据字典文件,所以采用联机数据字典:
EXECUTE dbms_logmnr.start_logmnr(OPTIONS => DBMS_LOGMNR.DICT_FROM_ONLINE_CATALOG);
-
将结果保存到中间表中
create table log1 as select * from v$logmnr_contents;
-
结束挖掘
EXECUTE DBMS_LOGMNR.END_LOGMNR;
-
查询丢失的数据
select * from suq_log2 where seg_owner='COMM' and seg_name='SS_DICT' and operation='DELETE' and username='EKACT'
这里主要是查询sql_undo字段,里面表示数据如果恢复需要执行的sql语句.
在这里我们拿到了sql_undo里面数据能直接进行恢复吗?是不行的,因为在挖掘的时候我们使用的是联机数据字典,此数据字典和我们恢复的归档日志中的字典信息不匹配.那么在sql_undo中的sql如下:
insert into "COMM"."SS_DICT"("COL 1","COL 2","COL 3","COL 4","COL 5") values (NULL,HEXTORAW('5936352e343030'),HEXTORAW('d7d4d0d0b0ceb3fdb5bcc4f2b9dc'),HEXTORAW('5a584243444e47'),HEXTORAW('545452424e4e54'));
因为客户只是对表结果进行了调整,所以在归档日志中还能识别出用户名和表名,但是字段名和字段值就不行了.
那么这里就需要对数据进行转换.
上面COL 1…COL 5这里需要客户提供,老的表还在,只是修改了表结构,还原出来即可.
数据里面归档日志挖掘出来为16进制数据,例如5936352e343030,那么需要使用UTL_RAW.CAST_TO_VARCHAR2来进行转换.
将HEXTORAW替换为UTL_RAW.CAST_TO_VARCHAR2后进行插入,恢复正常.