晚上一個客戶聯繫我,開發修改數據,誤刪除了一批數據(一千條左右),希望我能幫忙進行恢復.
首先詢問客戶是使用了什麼方法來刪除數據的,不同的刪除方法恢復的難易程度不同.
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後進行插入,恢復正常.