------------------------------------------2015-07-09-----------------------------------------------
一、背景
首先,在我库上完全没有用过oracle logminer的情况下,删除了一张表:
SQL> delete from test1;
已删除40行。
SQL> commit;
提交完成。
然后我心痛的后悔了,但是我又没有开启flashback,做不完全恢复成本太高,所以想使用oracle logminer来挖掘出当时是语句和回滚语句。
二.oracle logminer介绍与使用(很多资料都是摘自于网络,实验部分的内容则是根据我自身环境情况进行修改)
Logminer是oracle从8i开始提供的用于分析重做日志信息的工具,它包括DBMS_LOGMNR和DBMS_LOGMNR_D两个package,后边的D是字典的意思。
它既能分析redo log file,也能分析归档后的archive log file。在分析日志的过程中需要使用数据字典,一般先生成数据字典文件后使用,10g版本还可以使用在线数据字典。
Logminer也可以分析其它数据库的重做日志文件,但是必须使用重做日志所在数据库的数据字典,否则会出现无法识别的乱码。
另外被分析数据库的操作系统平台最好和当前Logminer所在数据库的运行平台一样,且block size相同。
2.1 安装oracle logminer
在使用LogMiner之前需要确认Oracle是否带有进行LogMiner分析包,一般来说Windows操作系统Oracle10g以上都默认包含。
如果不能确认,可以DBA身份登录系统,查看系统中是否存在运行LogMiner所需要的dbms_logmnr、dbms_logmnr_d包,如果没有需要安装LogMiner工具,必须首先要运行下面这样两个脚本:
1、$ORACLE_HOME/rdbms/admin/dbmslm.sql
2、$ORACLE_HOME/rdbms/admin/dbmslmd.sql.
这两个脚本必须均以DBA用户身份运行。其中第一个脚本用来创建DBMS_LOGMNR包,该包用来分析日志文件。第二个脚本用来创建DBMS_LOGMNR_D包,该包用来创建数据字典文件。
创建完毕后将包含如下存储过程和视图:
类型 名称 用途
存储过程 Dbms_logmnr_d.build 创建一个数据字典文件
存储过程 Dbms_logmnr.add_logfile 在列表中增加日志文件以供分析
存储过程 Dbms_logmnr.start_logmnr 使用一个可选的字典文件和前面确定要分析日志文件来启动LogMiner
存储过程 Dbms_logmnr.end_logmnr 停止LogMiner分析
视图 V$logmnr_dictionary 显示用来决定对象ID名称的字典文件的信息
视图 V$logmnr_logs 在LogMiner启动时显示分析的日志列表
视图 V$logmnr_contents LogMiner启动后,可以使用该视图在SQL提示符下输入SQL语句来查询重做日志的内容
2.2 创建数据字典文件
LogMiner工具实际上是由两个新的PL/SQL内建包((DBMS_LOGMNR 和 DBMS_ LOGMNR_D)和四个V$动态性能视图(视图是在利用过程DBMS_LOGMNR.START_LOGMNR启动LogMiner时创建)组成。
在使用LogMiner工具分析redo log文件之前,可以使用DBMS_LOGMNR_D 包将数据字典导出为一个文本文件。
该字典文件是可选的,但是如果没有它,LogMiner解释出来的语句中关于数据字典中的部分(如表名、列名等)和数值都将是16进制的形式,我们是无法直接理解的。
例如,下面的sql语句:
INSERT INTO dm_dj_swry (rydm, rymc) VALUES (00005, '张三');
LogMiner解释出来的结果将是下面这个样子:
insert into Object#308(col#1, col#2) values (hextoraw('c30rte567e436'), hextoraw('4a6f686e20446f65'));
创建数据字典的目的就是让LogMiner引用涉及到内部数据字典中的部分时为他们实际的名字,而不是系统内部的16进制。数据字典文件是一个文本文件,使用包DBMS_LOGMNR_D来创建。
如果我们要分析的数据库中的表有变化,影响到库的数据字典也发生变化,这时就需要重新创建该字典文件。
另外一种情况是在分析另外一个数据库文件的重作日志时,也必须要重新生成一遍被分析数据库的数据字典文件。
创建数据字典文件之前需要配置LogMiner文件夹:
1 CREATE DIRECTORY utlfile AS 'c:\oracle\logmnr';
2 alter system set utl_file_dir ='c:\oracle\logmnr' scope=spfile; (这样改这个参数岂不是要重启数据库??实验环境无所谓,生产环境可就麻烦了,我就暂时先不重启了)
创建数据字典文件:
EXECUTE dbms_logmnr_d.build(dictionary_filename => 'dictionary.ora', dictionary_location =>'c:\oracle\logmnr');
*
第 1 行出现错误:
ORA-01308: 未设置初始化参数 utl_file_dir
ORA-06512: 在 "SYS.DBMS_LOGMNR_INTERNAL", line 5786
ORA-06512: 在 "SYS.DBMS_LOGMNR_INTERNAL", line 5876
ORA-06512: 在 "SYS.DBMS_LOGMNR_D", line 12
ORA-06512: 在 line 1
好吧,果然utl_file_dir这个参数没有生效。岂不是我还是要重启数据库??
先重启数据库,完成实验,熟悉后再细致分析下,可否跳过创建数据字典文件这一步。
重启数据库:
SQL> shutdown immediate
数据库已经关闭。
已经卸载数据库。
ORACLE 例程已经关闭。
SQL> startup
ORACLE 例程已经启动。
Total System Global Area 585879552 bytes
Fixed Size 1348844 bytes
Variable Size 218106644 bytes
Database Buffers 360710144 bytes
Redo Buffers 5713920 bytes
数据库装载完毕。
数据库已经打开。
再次执行创建数据字典文件:(等了有十几秒钟,执行成功)
SQL> EXECUTE dbms_logmnr_d.build(dictionary_filename => 'dictionary.ora', dictionary_location =>'c:\oracle\logmnr');
PL/SQL 过程已成功完成。
2.3 加入需分析的日志文件
Oracle的LogMiner可以分析在线(online)和归档(offline)两种日志文件,加入分析日志文件使用dbms_logmnr.add_logfile过程,
第一个文件使用dbms_logmnr.NEW参数,后面文件使用dbms_logmnr.ADDFILE参数。
1、创建列表
BEGIN
dbms_logmnr.add_logfile(logfilename=>'C:\APP\ADMINISTRATOR\ORADATA\ORCL\REDO03.LOG',
options=>dbms_logmnr.NEW);
END;
/
2、添加其他日志文件到列表
BEGIN
dbms_logmnr.add_logfile(logfilename=>'C:\oracle\archive\ARC00033_0880122604.001',
options=>dbms_logmnr.ADDFILE);
dbms_logmnr.add_logfile(logfilename=>'C:\oracle\archive\ARC00034_0880122604.001',
options=>dbms_logmnr.ADDFILE);
END;
/
2.4 使用LogMiner进行日志分析
Oracle的LogMiner分析时分为无限制条件和限制条件两种,无限制条件中分析所有加入到分析列表日志文件,限制条件根据限制条件分析指定范围日志文件。
1、无限制条件
EXECUTE dbms_logmnr.start_logmnr(dictfilename=>'c:\oracle\logmnr\dictionary.ora');
2、有限制条件
通过对过程DBMS_ LOGMNR.START_LOGMNR中几个不同参数的设置,可以缩小要分析日志文件的范围。通过设置起始时间和终止时间参数我们可以限制只分析某一时间范围的日志。
如下面的例子,我们仅仅分析2015年7月9日的日志,:
EXECUTE dbms_logmnr.start_logmnr(
dictfilename=>'c:\oracle\logmnr\dictionary.ora',
StartTime =>to_date('2015-07-09 00:00:00','YYYY-MM-DD HH24:MI:SS')
EndTime =>to_date(''2015-07-09 23:59:59','YYYY-MM-DD HH24:MI:SS '));
也可以通过设置起始SCN和截至SCN来限制要分析日志的范围:
EXECUTE dbms_logmnr.start_logmnr(
dictfilename=>'c:\oracle\logmnr\dictionary.ora',
StartScn =>20,
EndScn =>50);
我这边执行一次无限制条件分析:
SQL> EXECUTE dbms_logmnr.start_logmnr(dictfilename=>'c:\oracle\logmnr\dictionary.ora');
PL/SQL 过程已成功完成。
SQL>
2.5 观察分析结果
到现在为止,我们已经分析得到了重作日志文件中的内容。动态性能视图v$logmnr_contents包含LogMiner分析得到的所有的信息。
SELECT sql_redo FROM v$logmnr_contents;
如果我们仅仅想知道某个用户对于某张表的操作,可以通过下面的SQL查询得到,该查询可以得到用户LOGMINER对表EMP所作的一切工作。
SELECT sql_redo FROM v$logmnr_contents WHERE username='LOGMINER' AND tablename='EMP';
需要强调一点的是,视图v$logmnr_contents中的分析结果仅在我们运行过程'dbms_logmrn.start_logmnr'这个会话的生命期中存在。
这是因为所有的LogMiner存储都在PGA内存中,所有其他的进程是看不到它的,同时随着进程的结束,分析结果也随之消失。
最后,使用过程DBMS_LOGMNR.END_LOGMNR终止日志分析事务,此时PGA内存区域被清除,分析结果也随之不再存在。
执行了如下语句:结果太多,只好添加where条件限制
SELECT sql_redo,sql_undo FROM v$logmnr_contents;
修改后:
SELECT sql_redo,sql_undo FROM v$logmnr_contents where username='KING' and tablename='TEST1'; ---妹的,根本没有tablename这个字段呀
再次执行:
SQL> select sql_redo,sql_undo from v$logmnr_contents where username='KING';
未选定行
晕,竟然没有数据,难道是我添加分析日志那一块出问题了。。。
先停掉刚才的日志分析,重新分析一个归档日志看看
SQL> exec DBMS_LOGMNR.END_LOGMNR;
PL/SQL 过程已成功完成。
BEGIN
dbms_logmnr.add_logfile(logfilename=>'C:\app\Administrator\flash_recovery_area\ORCL\ARCHIVELOG\2015_07_09\O1_MF_1_32_BSVQ8WY8_.ARC',
options=>dbms_logmnr.NEW);
END;
/
结果还是没有,,我去,我怒了,,重新删表再来一遍!
--------------------------------------------------------------
查看当前redo日志:是2号日志组
SQL> select group#,status from v$log;
GROUP# STATUS
---------- ----------------
1 INACTIVE
2 CURRENT
3 INACTIVE
SQL>
另开一个窗口,用king用户登录,执行删除表操作:
SQL> conn king
输入口令:
已连接。
SQL> delete from test2;
已删除3091行。
SQL> commit;
提交完成。
SQL>
查看当前日志情况:依然是2号日志组,那么我现在就分析2号日志组的redo日志
SQL> select group#,status from v$log;
GROUP# STATUS
---------- ----------------
1 INACTIVE
2 CURRENT
3 INACTIVE
SQL>
------------------------------------------------------------------
SQL> select member from v$logfile where group#=2;
MEMBER
-------------------------------------------------------------------------------------------------------------------------------------------------------
C:\APP\ADMINISTRATOR\ORADATA\ORCL\REDO02.LOG
添加日志文件:
BEGIN
dbms_logmnr.add_logfile(logfilename=>'C:\APP\ADMINISTRATOR\ORADATA\ORCL\REDO02.LOG',options=>dbms_logmnr.NEW);
END;
/
执行日志分析:
EXECUTE dbms_logmnr.start_logmnr(dictfilename=>'c:\oracle\logmnr\dictionary.ora');
查看分析结果:
select sql_redo,sql_undo from v$logmnr_contents where username='KING';
SQL> select sql_redo,sql_undo from v$logmnr_contents where username='KING';
未选定行
SQL>
大爷的,为啥还没有!!!!肯定是我查找的方式不对,继续排查下。。
select sql_redo,sql_undo from v$logmnr_contents where table_name='TEST2'; ---AND SQL_UNDO <>'Unsupported'; ---Unsupported
果然是我之前查的有问题,现在就可以查出数据了,但是因为我是delete方式,查出来的数据结果有些多,
由于是使用sqlplus方式看着很难受,打算重新做下,并将v$logmnr_contents内容查到另外一张表中,然后再用plsql developer查看,权当熟悉步骤了
----------------------------------------------------------------------------------------------------------------------------------------------------------
具体步骤整理如下:(看了些其他人的案例,对于创建数据字典这块确实是需要的,也就意味着,需要重启数据库,这样的话,logminer应用起来有一定的限制了,毕竟不是所有生产库都可以随便启动的)
一、创建数据字典文件:
1 CREATE DIRECTORY utlfile AS 'c:\oracle\logmnr';
2 alter system set utl_file_dir ='c:\oracle\logmnr' scope=spfile;
EXECUTE dbms_logmnr_d.build(dictionary_filename => 'dictionary.ora', dictionary_location =>'c:\oracle\logmnr');
二.添加需要分析的日志文件:
1、创建列表
BEGIN
dbms_logmnr.add_logfile(logfilename=>'C:\APP\ADMINISTRATOR\ORADATA\ORCL\REDO01.LOG',options=>dbms_logmnr.NEW);
END;
/
2、添加其他日志文件到列表
exec dbms_logmnr.add_logfile(logfilename=>'C:\oracle\archive\ARC00056_0880122604.001',
options=>dbms_logmnr.ADDFILE);
exec dbms_logmnr.add_logfile(logfilename=>'C:\oracle\archive\ARC00057_0880122604.001',
options=>dbms_logmnr.ADDFILE);
exec dbms_logmnr.add_logfile(logfilename=>'C:\oracle\archive\ARC00058_0880122604.001',
options=>dbms_logmnr.ADDFILE);
三.分析日志文件:
1、无限制条件
EXECUTE dbms_logmnr.start_logmnr(dictfilename=>'c:\oracle\logmnr\dictionary.ora');
2、有限制条件
如下面的例子,我们仅仅分析2015年7月9日的日志,:
EXECUTE dbms_logmnr.start_logmnr(dictfilename=>'c:\oracle\logmnr\dictionary.ora', StartTime =>to_date('2015-07-13 10:18:00','YYYY-MM-DD HH24:MI:SS'),EndTime =>to_date('2015-07-13 10:23:00','YYYY-MM-DD HH24:MI:SS '));
也可以通过设置起始SCN和截至SCN来限制要分析日志的范围:
EXECUTE dbms_logmnr.start_logmnr(
dictfilename=>'c:\oracle\logmnr\dictionary.ora',
StartScn =>20,
EndScn =>50);
四、查看分析结果:(将v$logmnr_contents内容插入到一张临时表中)
create table temp_logmnr as select * from v$logmnr_contents;
select sql_redo,sql_undo from temp_logmnr where table_name='TEST2';
五、停掉日志分析:
exec DBMS_LOGMNR.END_LOGMNR;
-----------------------------------------------------------------------------------------------------------------------------------------------
查看当前日志文件:还是2号日志
SQL> select group#,status from v$log;
GROUP# STATUS
---------- ----------------
1 INACTIVE
2 CURRENT
3 INACTIVE
SQL> select group#,member from v$logfile where group#=2;
GROUP#
----------
MEMBER
--------------------------------------------------------------------------------
2
C:\APP\ADMINISTRATOR\ORADATA\ORCL\REDO02.LOG
查看下test3表情况:无重复数据
SQL> select count(*) from test3;
COUNT(*)
----------
3091
SQL> select count(distinct deptseq) from test3;
COUNT(DISTINCTDEPTSEQ)
----------------------
3091
使用king用户delete该表:
SQL> delete from test3;
已删除3091行。
SQL> commit;
提交完成。
SQL>
再次查看当前redo日志情况:还是2号日志
SQL> select group#,status from v$log;
GROUP# STATUS
---------- ----------------
1 INACTIVE
2 CURRENT
3 INACTIVE
------------------------------------------------------------------------------------------------------------------------------------------------
按照上面logminer的步骤重新做了一遍,出现了一个问题,就是分析后查出来的结果是小于被删除的数据量。
test3原表数据量是3091,但我日志挖掘出来的delete语句和对应的insert语句是3059条,比较郁闷了,难道是因为没有添加补充日志的原因??不应该啊。。
------------------------------------------------------------------------------------------------------------------------------------------------
生命在于折腾啊,来,再做个实验:先正常删除t1表,如果日志挖掘出来的数据量还不对,那么开启补充日志,再删一次t1的备表。
SQL> create table t1_1 as select *from t1;
表已创建。
SQL> select count(*) from t1;
COUNT(*)
----------
69261
SQL>
-----------------------
当前日志:2号日志
SQL> select group#,status from v$log;
GROUP# STATUS
---------- ----------------
1 INACTIVE
2 CURRENT
3 INACTIVE
SQL>
删除t1表:
SQL> delete from t1;
已删除69261行。
SQL> commit;
提交完成。
------------当前日志变成3号了,说明需要用到2号的归档日志
SQL> select group#,status from v$log;
GROUP# STATUS
---------- ----------------
1 INACTIVE
2 ACTIVE
3 CURRENT
-------------------------------------
日志挖掘出来的结果数是69246条,还是少数据了!!郁闷。。。
一下子没有心思弄这个了,明天状态好继续搞吧,
------------------------------------------------------------------------------------------------------------------------------------------
大爷的,还报了600的错误:
ORA-00600: 内部错误代码, 参数: [krvxrrnrl], [4], [0x412D0954], [], [], [], [],
[], [], [], [], []
-----------------------------------------------------------------------------------------------------------------------------------------------
2015-07-13
添加了补充日志后,再做实验,发现查出来的数据符合我删除的数据量,但是当我alter database drop supplemental log data后,数据量又变少了,
我做实验的表是231条,取消掉补充日志后,logminer挖掘出来的结果数只有199条。。。
这样我就比较迷惑了,我执行了一个delete操作,oracle肯定会写undo和redo,按说应该会将我所有的更改信息都记录下来的啊,怎么会在redo日志里面出现丢失的情况呢?
查了下文档,确实对于oracle 10g和11g而言,使用logminer需要开启supplemental log,否则会出现无法挖掘到dml语句的情况,而默认10g和11g默认情况下是没有开启的,
这也就意味着,对于很多生产环境而言,logminer的用武之地会有很多局限性。
就拿我做的实验而言,为了用到数据字典,需要设置utl参数,从而需要重启数据库,接着由于没有开启最小补充日志,导致挖掘出来的dml语句数量少于我delete 的数量,从而使得数据没有完全恢复。
-----------------------------------------------------------------------------------------------------------------------------------------------