注册
【与达梦同行】归档挖掘与数据恢复
技术分享/ 文章详情 /

【与达梦同行】归档挖掘与数据恢复

啊小周 2022/12/20 3103 1 1

1.背景

在实际项目中,可能会遇到误操作数据的情况,如果归档有保留的话,可以通过归档挖掘快速找回数据。

2.测试准备

在实际环境中,为了不影响运行的系统,一般需要将数据恢复到测试环境,在测试环境中找回数据再恢复到原本的系统中去。
思考:为什么需要还原一个对应的数据库
因为归档中记录的对象是根据对象id去获取的,如果是将归档拿到一个新的库上挖掘,是无法获取到对应的信息的。
数据库需要开启归档,并且开启逻辑附加日志

2.1 测试用例

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;


2.2 恢复数据

2.2.1 归档挖掘

1.添加需要分析的归档日志

call dbms_logmnr.add_logfile ('/home/dmdba/dm_138/dmarch/ARCHIVE_LOCAL2_0x114CCE12_EP0_2022-11-20_18-06-57.log');
  1. 分析归档:
    分析归档文件,指定开始时间和结束时间,也可以不指定
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;
image.png

用dmmdf修改归档文件的db_magic
image.png

最后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%';
  1. 拿出需要修复的语句后关闭归档分析
DBMS_LOGMNR.END_LOGMNR();

image.png

2.2.2 恢复update数据

将挖掘出来的语句格式进行调整,可以参考以下过程
需要注意的是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; 

2.2.3 恢复delete数据

恢复被误删除的数据,首先先创建个备份表,目的是将这些误删除的数据插入进该表

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;
评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服