在实际项目中,可能会遇到误操作数据的情况,如果归档有保留的话,可以通过归档挖掘快速找回数据。
在实际环境中,为了不影响运行的系统,一般需要将数据恢复到测试环境,在测试环境中找回数据再恢复到原本的系统中去。
思考:为什么需要还原一个对应的数据库
因为归档中记录的对象是根据对象id去获取的,如果是将归档拿到一个新的库上挖掘,是无法获取到对应的信息的。
数据库需要开启归档,并且开启逻辑附加日志
create table t1(id int primary key,c1 varchar2(36),c2 varchar2(36),c3 varchar2(36),c4 varchar2(36));
create table t2(id int primary key,c1 varchar2(36),c2 varchar2(36),c3 varchar2(36),c4 varchar2(36));
insert into t1 select level,dbms_random.string('u',36),dbms_random.string('u',36),dbms_random.string('u',36),dbms_random.string('u',36) from dual connect by level < 10000;
commit;
insert into t2 select level,dbms_random.string('u',36),dbms_random.string('u',36),dbms_random.string('u',36),dbms_random.string('u',36) from dual connect by level < 1000000;
commit;
dbms_stats.gather_table_stats(user,'T1');
dbms_stats.gather_table_stats(user,'T2');
误操作1:update c2列
update t1 set c2='ABCDFG' where c1 like 'F%';
commit;
误操作2:delete c1 like 'AF%' 的数据
delete from t2 where c1 like 'AF%';
commit;
1.添加需要分析的归档日志
call dbms_logmnr.add_logfile ('/home/dmdba/dm_138/dmarch/ARCHIVE_LOCAL2_0x114CCE12_EP0_2022-11-20_18-06-57.log');
DBMS_LOGMNR.START_LOGMNR(OPTIONS=>2130 , STARTTIME=>TO_DATE('2022-01-11 14:00:00','YYYY-MM-DD HH24:MI:SS') , ENDTIME=>TO_DATE('2022-01-13 18:00:00','YYYY-MM-DD HH24:MI:SS'));
DBMS_LOGMNR.START_LOGMNR(OPTIONS=>2130);
执行失败(语句1)
-712: 归档日志MAGIC错误
-712: DBMS_LOGMNR.START_LOGMNR line 12
这里报错的原因是因为这个库是还原库,归档的db_magic与当前还原库不一致,需要修改db_magic
查询还原库的db_magic
select db_magic from v$rlog;
用dmmdf修改归档文件的db_magic
最后y保存即可
移除刚才添加的归档日志
call dbms_logmnr.remove_logfile('/home/dmdba/dm_138/dmarch/ARCHIVE_LOCAL2_0x114CCE12_EP0_2022-11-20_18-06-57.log');
重新添加
call dbms_logmnr.add_logfile ('/home/dmdba/dm_138/dmarch/ARCHIVE_LOCAL2_0x114CCE12_EP0_2022-11-20_18-06-57.log');
分析归档
DBMS_LOGMNR.START_LOGMNR(OPTIONS=>2130);
3.查询v$LOGMNR_CONTENTS视图
将挖掘的结果保存到一个备份表中
create table LOG_UPDATE as
select sql_redo from v$LOGMNR_CONTENTS where sql_redo like '%UPDATE%';
create table LOG_DELETE as
select sql_redo from v$LOGMNR_CONTENTS where sql_redo like '%DELETE%';
DBMS_LOGMNR.END_LOGMNR();
将挖掘出来的语句格式进行调整,可以参考以下过程
需要注意的是ID是主键,如果主键是其他命名,要做相应替换
declare
cursor cur_redo
is
select
substr(sql_redo, 0, INSTR(SQL_REDO, 'SET')+LENGTHB('SET'))
||replace(replace(substr(sql_redo, instr(sql_redo, 'WHERE')+lengthb('WHERE'), lengthb(sql_redo)), 'AND', ','),'IS NULL','= NULL')
||' WHERE "ID" = '||substr(sql_redo,instr(sql_redo, '"ID" = ')+lengthb('"ID" = '),instr(sql_redo,'AND',1)-instr(sql_redo, '"ID" = ')-lengthb('"ID" = ')) sqlstr
from
LOG_UPDATE
where
SQL_REDO is not null;
i int;
BEGIN
i:=0;
for X in (cur_redo)
LOOP
i:=i+1;
--BEGIN
execute immediate x.sqlstr;
if i=1000 then -- 1000行提交一次
commit;
i:=0;
end if;
END LOOP;
COMMIT;
END;
恢复被误删除的数据,首先先创建个备份表,目的是将这些误删除的数据插入进该表
create table t2_bak as select * from t2 where 1=2;
先插入主键数据
declare cursor curedo is
select
'INSERT INTO T2_BAK(ID) values ('
||substr(sql_redo, instr(sql_redo, '"ID" =')+lengthb('"ID" ='), instr(sql_redo, 'AND', 1)-(instr(sql_redo, '"ID" =')+lengthb('"ID" =')))
||');' as sqlstr
from
log_delete where sql_redo is not null;
i int;
begin
i:=0;
for X in curedo loop
execute immediate X.sqlstr;
i:=i+1;
if i= 1000 then
commit;
i:=0;
end if;
end loop;
commit;
end;
再做update
declare cursor curedo is
select 'UPDATE T2_BAK SET '
||replace(substr(sql_redo,instr(sql_redo,'AND',1)+lengthb('AND')),'AND',',')||substr(sql_redo,instr(sql_redo,'WHERE')-1,instr(sql_redo,'AND')-instr(sql_redo,'WHERE')) sqlstr from log_delete
where sql_redo is not null;
i int;
begin
i:=0;
for X in curedo loop
execute immediate X.sqlstr;
i:=i+1;
if i= 1000 then
commit;
i:=0;
end if;
end loop;
commit;
end;
最后校验数据无误
插入数据到原表
insert into t2 select * from t2_bak;
文章
阅读量
获赞