一、案例
我们有一个同事,在测试环境内执行完DBMS_LOGMNR_D.SET_TABLESPACE(‘TBS1’)后,又执行了drop tablespace TBS1 INCLUDING CONTENTS AND DATAFILES;
结果导致系统包dbms_logmnr,DBMS_LOGMNR_INTERNAL,dbms_logmnr_d等不能正使用。
很不幸,测试环境我们没有备份,那应该如何处理呢?
二、问题
如何恢复dbms_logmnr,DBMS_LOGMNR_INTERNAL,dbms_logmnr_d的正常使用?
三、数据收集和分析
3.1 操作过程顾
CREATE TABLESPACE TBS1
DATAFILE '/u04/tbs101.dbf' SIZE 1g ;
EXECUTE SYS.DBMS_LOGMNR_D.SET_TABLESPACE('TBS1');
drop tablespace tbs1 including contents and datafiles;
CREATE TABLESPACE TBS2
DATAFILE '/u04/tbs201.dbf' SIZE 1g ;
EXECUTE SYS.DBMS_LOGMNR_D.SET_TABLESPACE('TBS2'); --这时开始报错,说DBMS_LOGMNR_INTERNAL有内部错误
分析:1. 查看DBMS_LOGMNR_INTERNAL、DBMS_LOGMNR_D、DBMS_LOGMNR状态,发现是invalid 初步猜测在set_tablespace时,将一些系统对象move到新表空间。
3.2 实验验证
- 使用VM上的ORACLE DB
- 事前进行备份
- 执行SET_TABLESPACE,观察新表空间有些什么对象。
CREATE TABLESPACE TBS1
DATAFILE
'/u01/app/oracle/oradata/db1/tbs101.dbf' SIZE 500m;
EXECUTE SYS.DBMS_LOGMNR_D.SET_TABLESPACE('TBS1');
select owner,segment_name,segment_type from dba_segments where tablespace_name='TBS1';
有148个由logmnr开头组成的表和索引,有分区表,有LOB对象,
- 模拟删除
drop tablespace tbs1 including contents and datafiles;
CREATE TABLESPACE TBS2
DATAFILE
'/u01/app/oracle/oradata/db1/tbs201.dbf' SIZE 500m;
EXECUTE SYS.DBMS_LOGMNR_D.SET_TABLESPACE('TBS2');
SQL> EXECUTE SYS.DBMS_LOGMNR_D.SET_TABLESPACE('TBS2');
BEGIN SYS.DBMS_LOGMNR_D.SET_TABLESPACE('TBS2'); END;
*
ERROR at line 1:
ORA-04063: package body "SYS.DBMS_LOGMNR_INTERNAL" has errors
ORA-06508: PL/SQL: could not find program unit being called: "SYS.DBMS_LOGMNR_INTERNAL"
ORA-06512: at "SYS.DBMS_LOGMNR_D", line 135
ORA-06512: at line 1
select * from dba_errors;
由于删除tbs1时,把一些system下logmnr开头的对象都删除了,所以在调用关联的包时表报错了。
- 模拟恢复(support.oracle.com上提供了类似解决方案)
--shutdown the database.
shutdown immediate
--start the db
STARTUP UPGRADE
--spool to a file
SPOOL patch.log
--run the upgrade script
@?/rdbms/admin/catupgrd.sql
--turn off spooling (review file for errors)
spool off
--shutdown and restart.
shutdown immediate
startup
EXECUTE SYS.DBMS_LOGMNR_D.SET_TABLESPACE('TBS2');
-- 依样报错,信息和之前的一样,恢复失败。
- 实验结果 恢复失败
3.3 第二次实验验证
- 实验方案:从其它正常的库里把缺少的对象抽出DDL,重执行。
- 模拟删除,步骤与3.2一样,就不再过多描述
- 根据3.2观察的误删除表空间上的都是对logmnr开头的对象,于是在一个的新环境上(正常的DB)重新执行一次dbms_logmnr_d.set_tablespace('TBS1')命令,然后把该tbs1上的对象导出ddl(方法有很多,可以用imp,exp命令,或toad的export ddl,这里就不过多描述),在误删表空间上重生成一次。
- 结果,可以正常调用dbms_logmnr包的命令,具体如下:
EXECUTE SYS.DBMS_LOGMNR_D.SET_TABLESPACE('TBS2');
EXECUTE dbms_logmnr.add_logfile(LogFileName=>'/XXXX/2018_04_26/o1_mf_1_404233_fg387zs2_.arc', Options=>dbms_logmnr.new);
EXECUTE dbms_logmnr.start_logmnr(options=>dbms_logmnr.DICT_FROM_ONLINE_CATALOG);
EXECUTE dbms_logmnr.end_logmnr();
select * from v$logmnr_contents;
- 成功运行没有问题。
3.4 第三次实验验证
- 方案:将备份转移到其它机上恢复,然后执行3.3步骤。
- 异机恢复过程与步骤也不过多描述了,这些都可以网上搜索到。
- 验证通过,成功运行。
四、结论
- 1.采用重运行脚本的方式基本不可行。
- 2.如果没有备份或测试开发环境的情况下,就从异机导一份结构过来,内容可以不补充。
- 3.如果有备份,最后恢复出来,把误删除前的表空间及内容恢复,然后用imp,exp方法导回到原库。
- 最安全,最稳妥是3,其次是2.
五、延伸记录
alter system set utl_file_dir='/u03/' scope=spfile;
EXECUTE dbms_logmnr_d.build( dictionary_filename => 'logminer_test.dat', dictionary_location => '/u03/');
BEGIN SYS.DBMS_LOGMNR.START_LOGMNR( STARTSCN => 11886853829776, OPTIONS => DBMS_LOGMNR.DICT_FROM_REDO_LOGS+SYS.DBMS_LOGMNR.DDL_DICT_TRACKING + SYS.DBMS_LOGMNR.CONTINUOUS_MINE); END; --如果不执行1,2命令,会报字典不存在。