2020-05-18

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

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


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

案例背景:

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(*)-------------------------------- ----------          6457OBJ# 292119       20385OBJ# 292109       71354OBJ# 292117       55421STATS_TARGET$       140WRI$_OPTSTAT_IND_HISTORY     68CDEF$         44OBJ# 292112       143545OBJ# 292124       85876WRI$_ADV_OBJECTS       12OBJ# 292116       26929OBJ# 292123       85876SEQ$          2SEG$         2587TAB$         139WRI$_OPTSTAT_HISTHEAD_HISTORY   2442OBJ# 292121       112601WRI$_SEGADV_OBJLIST      6HIST_HEAD$        1221MON_MODS_ALL$       46DBMS_TABCOMP_TEMP_CMP     12SDO_GEOR_DDL_$$     12WRI$_ADV_FINDINGS       6DEFERRED_STG$        6OBJ# 292122       112601CCOL$         44WRI$_ADV_MESSAGE_GROUPS     12OBJ# 292118       55421WRI$_OPTSTAT_HISTGRM_HISTORY   7143OBJ$          35CON$          66WRI$_ADV_ACTIONS       12WRI$_ADV_REC_ACTIONS      6OBJ# 292120       20385WRI$_OPTSTAT_TAB_HISTORY     47DBMS_TABCOMP_TEMP_UNCMP     12IND$          72HISTGRM$        14371COL$         1224LOB$          4WRI$_ADV_RECOMMENDATIONS     12OBJ# 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 24","COL 25","COL 26","COL 27","COL 28","COL 29","COL 30","COL 31","COL 32","COL33","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","COL 51","COL 52","COL 53","COL 54","COL 55","COL 56","COL 57","COL 58","COL 59","COL 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('d3a6b8b6c6b1beddbde1cbe3b7bdcabdb6d4d3a6d2f8d0d0d5cbbba7'),HEXTORAW('303035474756'),NULL,NULL,NULL,HEXTORAW('c102'),HEXTORAW('80'),HEXTORAW('c102'),HEXTORAW('c102'),NULL,NULL,NULL,NULL,NULL,NULL,HEXTORAW('4e'),NULL,HEXTORAW('3e1166'),HEXTORAW('c102'),NULL,HEXTORAW('3232303131303031433131303030303030303036344a425231303031433131303030303030303036344c3841313030314331313030303030303030354e30344f31'),NULL,NULL,HEXTORAW('c102'),HEXTORAW('b9f3d6ddb8bbb4efbae3c9ccc3b3d3d0cfdeb9abcbbe'),NULL,HEXTORAW('3e1166'),HEXTORAW('313030314331313030303030303030354e30344f'),HEXTORAW('313030314331313030303030303030354d5a5549'),HEXTORAW('313030314331313030303030303030385a545741'),HEXTORAW('3130303143313130303030303030303634443159'),HEXTORAW('3130303143313130303030303030303634455947'),HEXTORAW('46332d4378782d3031'),HEXTORAW('7e'),HEXTORAW('7e'),NULL,HEXTORAW('313030325a303130303030303030303030314b31'),HEXTORAW('31303031433131303030303030303030324a3357'),HEXTORAW('303030314331313030303030303030303041364d'),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$'  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'''||'%' ;

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。

3 开始还原数据

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

4,案例总结

1,生产环境一定要有RMAN的备份。

2,X86环境一定要搭建一个容灾环境,哪怕是用普通的PC服务器也一定要有一个ADG容灾环境。

3,IMPDP导入时,切记不要在生产环境操作。

4,IMPDP只是作为数据迁移和数据备份与还原的工具,并不是数据库备份与还原工具。

5,如果真出现问题,不会操作不要慌,联系我们公司专家,千万不要2次破坏。

*************************************作者介绍*****************************************

原创作者:认真就输

联系电话:18081072613 微信、QQ:18081072613

转载就注明出处

*************************************************************************************


No comments:

Post a Comment