当前位置: 首页 > ORACLE, 异常恢复 > 正文

impdp加上table_exists_action=replace覆盖数据后的恢复方法

案例背景:

2020年5月的一天,某客户将数据库中的业务核心用户NC65导入数据库中。不幸的是事情就注定要发生,导入的目标数据库是当前运行的生产数据库,并且在impdp导入时,添加了table_exists_action=replace选项(此选项时到impdp导入对象时,如发现对象已经存在,就会先删除对象,再创建对象)。来了,不幸的事情发生了,生产环境的NC65用户数据被还原到备份时的状态,最近几天新产生的数据被清空,惨了,要被领导批了。此生产环境中数据库还无rman备份、在导入前也没有做expdp备份,不幸的事情真是这么相似。但幸运的是数据库运行在归档模式,并且有完整的最近7天的归档日志。此时可以在归档日志上面动动手脚,利用归档日志来手动前滚数据库。

恢复思路

1,利用归档日志手动前滚

数据库可以利用归档日志前滚,此时我们只能利用归档日志手动前滚。logminer是Oracle官方推出的用于分析日志的脚本,可以将日志中的DML操作解析成SQL语句,是否真如大家想的这样呢?肯定不是的,此时问题来了,Oracle日志中不会记录对象的名字,只记录对象的id号,并且此环境中impdp在导入时已经重建相关对象,所以对象id号码已经发生了变化,此时给logminer添加了不少困难。logmnr分析结果如下图

SQL> select table_name,count(*) from V$LOGMNR_CONTENTS group by table_name;

TABLE_NAME                         COUNT(*)
-------------------------------- ----------
                                       6457
OBJ# 292119                           20385
OBJ# 292109                           71354
OBJ# 292117                           55421
STATS_TARGET$                           140
WRI$_OPTSTAT_IND_HISTORY                 68
CDEF$                                    44
OBJ# 292112                          143545
OBJ# 292124                           85876
WRI$_ADV_OBJECTS                         12
OBJ# 292116                           26929
OBJ# 292123                           85876
SEQ$                                      2
SEG$                                   2587
TAB$                                    139
WRI$_OPTSTAT_HISTHEAD_HISTORY          2442
OBJ# 292121                          112601
WRI$_SEGADV_OBJLIST                       6
HIST_HEAD$                             1221
MON_MODS_ALL$                            46
DBMS_TABCOMP_TEMP_CMP                    12
SDO_GEOR_DDL_$                    12
WRI$_ADV_FINDINGS                         6
DEFERRED_STG$                             6
OBJ# 292122                          112601
CCOL$                                    44
WRI$_ADV_MESSAGE_GROUPS                  12
OBJ# 292118                           55421
WRI$_OPTSTAT_HISTGRM_HISTORY           7143
OBJ$                                     35
CON$                                     66
WRI$_ADV_ACTIONS                         12
WRI$_ADV_REC_ACTIONS                      6
OBJ# 292120                           20385
WRI$_OPTSTAT_TAB_HISTORY                 47
DBMS_TABCOMP_TEMP_UNCMP                  12
IND$                                     72
HISTGRM$                              14371
COL$                                   1224
LOB$                                      4
WRI$_ADV_RECOMMENDATIONS                 12
OBJ# 292115                           26929

logminer解析出来的SQL语句如下:

sql_redo大致如下,不仅对象名无法解析,字段名,字段类型均无法解析,所以sql_redo显示的时候只能使用COL1,COL2这样去显示字段名,使用HEXTORAW()实际存储值去显示字段值。

insert into "UNKNOWN"."OBJ# 292120"("COL 1","COL 2","COL 3","COL 4","COL 5","COL
 6","COL 7","COL 8","COL 9","COL 10","COL 11","COL 12","COL 13","COL 14","COL 15
","COL 16","COL 17","COL 18","COL 19","COL 20","COL 21","COL 22","COL 23","COL 2
4","COL 25","COL 26","COL 27","COL 28","COL 29","COL 30","COL 31","COL 32","COL
33","COL 34","COL 35","COL 36","COL 37","COL 38","COL 39","COL 40","COL 41","COL
 42","COL 43","COL 44","COL 45","COL 46","COL 47","COL 48","COL 49","COL 50","CO
L 51","COL 52","COL 53","COL 54","COL 55","COL 56","COL 57","COL 58","COL 59","C
OL 60","COL 61","COL 62","COL 63","COL 64","COL 65","COL 66","COL 67","COL 68","
COL 69","COL 70","COL 71","COL 72","COL 73","COL 74","COL 75","COL 76","COL 77",
"COL 78","COL 79","COL 80","COL 81","COL 82") values (HEXTORAW('d3a6b8b6c6b1bedd
bde1cbe3b7bdcabdb6d4d3a6d2f8d0d0d5cbbba7'),HEXTORAW('303035474756'),NULL,NULL,NU
LL,HEXTORAW('c102'),HEXTORAW('80'),HEXTORAW('c102'),HEXTORAW('c102'),NULL,NULL,N
ULL,NULL,NULL,NULL,HEXTORAW('4e'),NULL,HEXTORAW('3e1166'),HEXTORAW('c102'),NULL,
HEXTORAW('3232303131303031433131303030303030303036344a42523130303143313130303030
3030303036344c3841313030314331313030303030303030354e30344f31'),NULL,NULL,HEXTORA
W('c102'),HEXTORAW('b9f3d6ddb8bbb4efbae3c9ccc3b3d3d0cfdeb9abcbbe'),NULL,HEXTORAW
('3e1166'),HEXTORAW('313030314331313030303030303030354e30344f'),HEXTORAW('313030
314331313030303030303030354d5a5549'),HEXTORAW('313030314331313030303030303030385
a545741'),HEXTORAW('3130303143313130303030303030303634443159'),HEXTORAW('3130303
143313130303030303030303634455947'),HEXTORAW('46332d4378782d3031'),HEXTORAW('7e'
),HEXTORAW('7e'),NULL,HEXTORAW('313030325a303130303030303030303030314b31'),HEXTO
RAW('31303031433131303030303030303030324a3357'),HEXTORAW('3030303143313130303030
30303030303041364d'),NULL,NULL,NULL,NULL,NULL,NULL,HEXTORAW('3130303143313130303
...

2,重构数据字典

需要让logminer工具能正常解析对象,那么第一步就是需要恢复数据字典的内容,此时只能手动重构logminer字典信息,然后通过构造的字典信息去解析归档日志,得到正确的sql_redo。

那么如何去构造logminer字典信息呢?首先介绍一下logminer字典的来源。
通过dbms_logmnr_d.build去创建一个字典文件,对字典文件分析发现字典的数据来源为

OBJ$ 
TAB$
COL$
TS$
IND$
USER$
TABPART$
INDPART$
TABSUBPART$
TABCOMPART$
TYPE$
COLTYPE$
ATTRIBUTE$
LOB$
CDEF$
CCOL$
ICOL$
ATTRCOL$
NTAB$
OPQTYPE$
SUBCOLTYPE$
KOPM$
PROPS$
REFCON$
PARTOBJ$

那么我们只需要把数据字典基表数据还原到impdp误操作之前即可正确解析出sql_redo,这里采用的是方法是新建一个用户TEST,然后将生产用户NC65的8号dmp文件导入到TEST里,由于表结构这些与生产用户完全一样,

那么我们就可以利用这个新建用户去构造出删除之前的对象元数据信息。只需要OBJ$,COL$,TAB$,LOB$信息无误,就能解析出正确的sql_redo。构造大致过程如下:

2.1 分析出删除之前的表的OBJ#

方法最简单的是通过闪回查询,很不幸的是我们使用闪回查询时ORA-01555了,通过logminer去分析impdp时刻的归档日志,对OBJ$进行分析,则可以找到NC65的所有对象删除前的OBJ$信息,并保存在表OBJ_NC65里
创建表OBJ_NC65用于保留impdp误操作前NC65的表的OBJ$信息:

create table OBJ_NC65 as select * from obj$ where 1=2;

delete的sql_undo为insert,将下面sql查询出来的sql插入到OBJ_NC65表中:
 select SQL_UNDO from V$LOGMNR_CONTENTS 
  where table_name='OBJ

2.2.owner#=97为TEST,首先通过表名关联,找到obj#对应关系

create table t as select b.obj# obj_old,b.name,a.obj# obj_new,a.owner# from obj$ a,obj_nc65 b where a.name=b.name and a.owner#=97;

2.3.更新新建用户TEST的元数据信息,构造出能够正确解析的logminer字典信息

update obj$ a set obj#=(select obj_old from t where t.obj_new=a.obj#) where obj# in (select obj_new from t);
update col$ a set obj#=(select obj_old from t where t.obj_new=a.obj#) where obj# in (select obj_new from t);
update lob$ a set obj#=(select obj_old from t where t.obj_new=a.obj#) where obj# in (select obj_new from t);
update tab$ a set obj#=(select obj_old from t where t.obj_new=a.obj#) where obj# in (select obj_new from t);

2.4.刷新shared pool

alter system flush shared_pool;

2.5.解析归档

找出归档开始解析,只保留commit成功的记录,并把输出保存在表logmnr_new中,这里注意,如果需要分析的归档过多,建议分批次去做分析。否则会出现ORA-04030。
sqlplus “/as sysdba” << EOF

exec sys.dbms_logmnr.add_logfile('/u02/ncgdlog/1_10131_1012773265.dbf',sys.dbms_logmnr.new);
exec sys.dbms_logmnr.add_logfile('/u02/ncgdlog/1_10132_1012773265.dbf',sys.dbms_logmnr.addfile);
exec sys.dbms_logmnr.add_logfile('/u02/ncgdlog/1_10133_1012773265.dbf',sys.dbms_logmnr.addfile);
exec sys.dbms_logmnr.add_logfile('/u02/ncgdlog/1_10134_1012773265.dbf',sys.dbms_logmnr.addfile);
exec sys.dbms_logmnr.add_logfile('/u02/ncgdlog/1_10135_1012773265.dbf',sys.dbms_logmnr.addfile);
exec sys.dbms_logmnr.add_logfile('/u02/ncgdlog/1_10136_1012773265.dbf',sys.dbms_logmnr.addfile);
exec sys.dbms_logmnr.add_logfile('/u02/ncgdlog/1_10137_1012773265.dbf',sys.dbms_logmnr.addfile);
exec sys.dbms_logmnr.add_logfile('/u02/ncgdlog/1_10138_1012773265.dbf',sys.dbms_logmnr.addfile);
exec sys.dbms_logmnr.add_logfile('/u02/ncgdlog/1_10139_1012773265.dbf',sys.dbms_logmnr.addfile);
exec sys.dbms_logmnr.add_logfile('/u02/ncgdlog/1_10140_1012773265.dbf',sys.dbms_logmnr.addfile);
exec sys.dbms_logmnr.add_logfile('/u02/ncgdlog/1_10141_1012773265.dbf',sys.dbms_logmnr.addfile);
exec sys.dbms_logmnr.add_logfile('/u02/ncgdlog/1_10142_1012773265.dbf',sys.dbms_logmnr.addfile);
exec sys.dbms_logmnr.add_logfile('/u02/ncgdlog/1_10143_1012773265.dbf',sys.dbms_logmnr.addfile);
exec sys.dbms_logmnr.add_logfile('/u02/ncgdlog/1_10144_1012773265.dbf',sys.dbms_logmnr.addfile);
exec sys.dbms_logmnr.add_logfile('/u02/ncgdlog/1_10145_1012773265.dbf',sys.dbms_logmnr.addfile);
exec sys.dbms_logmnr.add_logfile('/u02/ncgdlog/1_10146_1012773265.dbf',sys.dbms_logmnr.addfile);
exec sys.dbms_logmnr.add_logfile('/u02/ncgdlog/1_10147_1012773265.dbf',sys.dbms_logmnr.addfile);
exec sys.dbms_logmnr.add_logfile('/u02/ncgdlog/1_10148_1012773265.dbf',sys.dbms_logmnr.addfile);
exec sys.dbms_logmnr.add_logfile('/u02/ncgdlog/1_10149_1012773265.dbf',sys.dbms_logmnr.addfile);
exec sys.dbms_logmnr.add_logfile('/u02/ncgdlog/1_10150_1012773265.dbf',sys.dbms_logmnr.addfile);
exec sys.dbms_logmnr.add_logfile('/u02/ncgdlog/1_10151_1012773265.dbf',sys.dbms_logmnr.addfile);
exec sys.dbms_logmnr.add_logfile('/u02/ncgdlog/1_10152_1012773265.dbf',sys.dbms_logmnr.addfile);
exec sys.dbms_logmnr.add_logfile('/u02/ncgdlog/1_10153_1012773265.dbf',sys.dbms_logmnr.addfile);
exec sys.dbms_logmnr.add_logfile('/u02/ncgdlog/1_10154_1012773265.dbf',sys.dbms_logmnr.addfile);
exec sys.dbms_logmnr.start_logmnr(options=>dbms_logmnr.dict_from_online_catalog+dbms_logmnr.committed_data_only);
create table logmnr_new tablespace users as select * from v\$logmnr_contents;
execute dbms_logmnr.end_logmnr;
quit;
EOF

2.6.删除TEST用户,并重建TEST,并导入NC65 8号的dmp。

and sql_redo like 'delete from%' and sql_redo like '%'||'"TYPE#" = ''2'''||'%' and sql_redo not like '%'||'"DATAOBJ#" IS NULL'||'%' and sql_redo like '%'||'"OWNER#" = ''92'''||'%' ;

3 开始还原数据

编写脚本按事务提交时间在TEST用户下去依次执行sql进行恢复。

4,案例总结

1,生产环境一定要有RMAN的备份。
2,X86环境一定要搭建一个容灾环境,哪怕是用普通的PC服务器也一定要有一个ADG容灾环境。
3,IMPDP导入时,切记不要在生产环境操作。
4,IMPDP只是作为数据迁移和数据备份与还原的工具,并不是数据库备份与还原工具。
5,如果真出现问题,不会操作不要慌,联系我们公司专家,千万不要2次破坏。

*************************************作者介绍*****************************************
原创作者:认真就输
联系电话:18081072613
微信、QQ:18081072613
转载就注明出处
*************************************************************************************

本文固定链接: http://www.htz.pw/?p=1189 | 认真就输

该日志由 huangtingzhong 于2020年05月18日发表在 ORACLE, 异常恢复 分类下, 通告目前不可用,你可以至底部留下评论。
原创文章转载请注明: impdp加上table_exists_action=replace覆盖数据后的恢复方法 | 认真就输
关键字: , , , , ,