logminer dictionary 跨庫分析歸檔日誌

原文地址:http://blog.itpub.net/53956/viewspace-1762461/

從9i開始oracle就推出了logminer--日誌挖掘技術,logminer可直接應用於redolog內容的解析、也被間接用於Flashback transaction、SQL apply in logical standby database等場合。

在對非本庫生成的redolog進行解析時,需要用到logminer dictionary,典型的例子:搭建logical standby database的過程中有一個步驟是build logminer dictionary。因爲primary db和logical standby db屬於完全不同的兩個庫,要使standby能夠從primary生成的redolog裏準確解析出可執行的sql語句,必須把primary db裏包含對象名稱信息的數據字典提供給standby db,提供的形式有基於文件和基於redolog兩種,前者是將數據字典信息直接導出到一個文本文件,後者將字典信息輸出到online redolog,簡單的說logminer dictionary就是包含對象名稱與對象ID間映射關係的數據字典表的子集,起到了將對象編號翻譯成對象名稱的作用。如果沒有logminer dictionary我們解析出來的sql裏將會充斥了各種晦澀難懂的數字和代號。
我們以解析一個歸檔日誌裏的SQL語句爲例,體驗一下logminer dictionary在其中所發揮的作用

/////////////////////
// 如果要解析的redolog是本庫產生的,那麼不需要build logminer dictionary,使用本庫的數據字典就行
/////////////////////
假設/oradata06/fra/TSTDB1/archivelog/2015_07_29/o1_mf_1_109_1l8oUn0kl_.arc是tstdb1庫生成的一個歸檔日誌文件,我們要在tstdb1庫上對這個歸檔進行解析,即在本庫進行logminer,無需額外build logminer dictionary,使用本庫現成的數據字典即可
###在archivelog產生的源庫tstdb1上執行logminer
SQL> show parameter db_name


NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
db_name                              string      tstdb1


exec dbms_logmnr.add_logfile(logfilename=>'/oradata06/fra/TSTDB1/archivelog/2015_07_29/o1_mf_1_109_1l8oUn0kl_.arc',options=>DBMS_LOGMNR.NEW);


***因爲是本庫所以使用DICT_FROM_ONLINE_CATALOG從數據字典裏檢索相關信息即可
exec DBMS_LOGMNR.START_LOGMNR(DictFileName=>'',Options=>dbms_logmnr.DICT_FROM_ONLINE_CATALOG);


***從Archivelog裏搜出的語句包含具體表名和列名,這些sql已經是經過翻譯的,因此是可執行的
set linesize 120 pagesize 140
SQL> select seg_name,sql_redo,sql_undo from V$LOGMNR_CONTENTS where seg_name='T0729_2';


SEG_NAME
------------------------------------------------------------------------------------------------------------------------
SQL_REDO
------------------------------------------------------------------------------------------------------------------------
SQL_UNDO
------------------------------------------------------------------------------------------------------------------------
T0729_2
create table scott.t0729_2 (id number,c2 varchar2(10)) tablespace omftbs1;




T0729_2
insert into "SCOTT"."T0729_2"("ID","C2") values ('1','a');
delete from "SCOTT"."T0729_2" where "ID" = '1' and "C2" = 'a' and ROWID = 'AAAKHHAAJAAAACVAAA';


T0729_2
insert into "SCOTT"."T0729_2"("ID","C2") values ('2','b');
delete from "SCOTT"."T0729_2" where "ID" = '2' and "C2" = 'b' and ROWID = 'AAAKHHAAJAAAACVAAB';


SQL> select * from V$LOGMNR_DICTIONARY;    <---沒有build logminer dictionary所以沒有記錄


no rows selected


set numwidth 16 linesize 120
col filename format a60
select low_scn,filename from V$LOGMNR_LOGS;  <---正在被挖掘的日誌
         LOW_SCN FILENAME
---------------- ------------------------------------------------------------
  12723365353557 /oradata06/fra/TSTDB1/archivelog/2015_07_29/o1_mf_1_109_1l8o
                 Un0kl_.arc


SQL> set numwidth 16
SQL> select start_scn,options from V$LOGMNR_PARAMETERS;   <---執行DBMS_LOGMNR.START_LOGMNR時指定的參數


       START_SCN          OPTIONS
---------------- ----------------
  12723365353557               16    <--16表示以本機的data dictionary作爲logminer dictionary


options取值及含義如下:
NO_DICT_RESET_ONSELECT    CONSTANT BINARY_INTEGER := 1;
COMMITTED_DATA_ONLY       CONSTANT BINARY_INTEGER := 2;
SKIP_CORRUPTION           CONSTANT BINARY_INTEGER := 4;
DDL_DICT_TRACKING         CONSTANT BINARY_INTEGER := 8;
DICT_FROM_ONLINE_CATALOG  CONSTANT BINARY_INTEGER := 16;
DICT_FROM_REDO_LOGS       CONSTANT BINARY_INTEGER := 32;
NO_SQL_DELIMITER          CONSTANT BINARY_INTEGER := 64;
PRINT_PRETTY_SQL          CONSTANT BINARY_INTEGER := 512;
CONTINUOUS_MINE           CONSTANT BINARY_INTEGER := 1024;
NO_ROWID_IN_STMT          CONSTANT BINARY_INTEGER := 2048;
STRING_LITERALS_IN_STMT   CONSTANT BINARY_INTEGER := 4096;  


/////////////////////
// 要解析來自於其它庫的redolog,在沒有logminer dictionary的情況下,無法解析出準確的、可執行的SQL語句
/////////////////////
在shzw庫上解析由tstdb1庫產生的archivelog:/oradata06/fra/TSTDB1/archivelog/2015_07_29/o1_mf_1_109_1l8oUn0kl_.arc
***將tstdb1庫產生的archivelog複製到目標庫shzw:
scp /oradata06/fra/TSTDB1/archivelog/2015_07_29/o1_mf_1_109_1l8oUn0kl_.arc [email protected]:/oradata04/


SQL> show parameter db_name


NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
db_name                              string      shzw


***沒有build logminer dictionary的情況下在shzw庫上解析tstdb1庫redolog所得到的sql語句沒有具體的表名和列名
exec dbms_logmnr.add_logfile(logfilename=>'/oradata04/o1_mf_1_109_1l8oUn0kl_.arc',options=>DBMS_LOGMNR.NEW);


exec DBMS_LOGMNR.START_LOGMNR(DictFileName=>'',Options=>0);


***以seg_name爲條件只搜到create table語句,ddl語句在沒有logminer dictionary的情況下能夠被正確解析出
set linesize 120 pagesize 140
SQL> select seg_name,sql_redo,sql_undo from V$LOGMNR_CONTENTS where seg_name='T0729_2';


SEG_NAME
------------------------------------------------------------------------------------------------------------------------
SQL_REDO
------------------------------------------------------------------------------------------------------------------------
SQL_UNDO
------------------------------------------------------------------------------------------------------------------------
T0729_2
create table scott.t0729_2 (id number,c2 varchar2(10)) tablespace omftbs1;


***改以rowid爲查找條件,DML沒有logminer dictionary的情況下無法得到其真實的名稱
select sql_redo,sql_undo from V$LOGMNR_CONTENTS where sql_redo like '%insert into%';
SQL_REDO
------------------------------------------------------------------------------------------------------------------------
SQL_UNDO
------------------------------------------------------------------------------------------------------------------------
。。。。。省略部分輸出


insert into "UNKNOWN"."OBJ# 41415"("COL 1","COL 2") values (HEXTORAW('c102'),HEXTORAW('61'));
delete from "UNKNOWN"."OBJ# 41415" where "COL 1" = HEXTORAW('c102') and "COL 2" = HEXTORAW('61') and ROWID = 'AAAKHHAAJA
AAACVAAA';


insert into "UNKNOWN"."OBJ# 41415"("COL 1","COL 2") values (HEXTORAW('c103'),HEXTORAW('62'));
delete from "UNKNOWN"."OBJ# 41415" where "COL 1" = HEXTORAW('c103') and "COL 2" = HEXTORAW('62') and ROWID = 'AAAKHHAAJA
AAACVAAB';




10 rows selected.


obj#41415正是t0729_2表在tstdb1庫上的object_id,insert語句裏的列名也被col 1、col 2替代了,插入的數值1被替換成了HEXTORAW('c102'),字符'a'被替換成了HEXTORAW('61'),可見沒有logminer dictionary除了ddl語句能完整顯示,dml語句根本不具備可讀性。引入logminer dictionary的目的就是能夠以可讀、可執行的形式顯示出這些SQL語句。

/////////////////////
// 構造logminer dictionary的兩種方式
/////////////////////
logminer dictionary可以存在於文本文件,也可以存在於redolog file,依然以shzw庫上解析tstdb1庫生成的redolog作爲例子
(1) 將logminer dictionary輸出到文本文件
***在源庫tstdb1上build logminer dictionary到某個文件裏
alter system set utl_file_dir='/oradata01' scope=spfile;


startup force


exec dbms_logmnr_d.build(dictionary_filename=>'tstdb1.dic',dictionary_location=>'/oradata01',options=>dbms_logmnr_d.STORE_IN_FLAT_FILE);


***把dictionary文件傳送到執行解析動作的shzw庫
scp /oradata01/tstdb1.dic [email protected]:/oradata04/


***在shzw庫上再次進行START_LOGMNR操作,這次看到了完整的表名和列名
exec dbms_logmnr.add_logfile(logfilename=>'/oradata04/o1_mf_1_109_1l8oUn0kl_.arc',options=>DBMS_LOGMNR.NEW);


exec DBMS_LOGMNR.START_LOGMNR(DictFileName=>'/oradata04/tstdb1.dic',Options=>0);   <---DictFileName指定了dictionary文件的路徑


set linesize 120 pagesize 140
select sql_redo,sql_undo from V$LOGMNR_CONTENTS


SQL_REDO
------------------------------------------------------------------------------------------------------------------------
SQL_UNDO
------------------------------------------------------------------------------------------------------------------------
。。。。省略部分輸出


insert into "SCOTT"."T0729_2"("ID","C2") values ('1','a');
delete from "SCOTT"."T0729_2" where "ID" = '1' and "C2" = 'a' and ROWID = 'AAAKHHAAJAAAACVAAA';


insert into "SCOTT"."T0729_2"("ID","C2") values ('2','b');
delete from "SCOTT"."T0729_2" where "ID" = '2' and "C2" = 'b' and ROWID = 'AAAKHHAAJAAAACVAAB';


commit;


我們來看一下logminer dictionary文件裏包含哪些內容,以上面生成的tstdb1.dic文件爲例,tstdb1.dic是一個文本文件,我們找出其中與t0729_2表有關的記錄
---記錄對象信息的OBJ$表
INSERT_INTO OBJ$_TABLE VALUES (41415,41415,36,'T0729_2',1,'',2,to_date('07/29/2015 14:11:23', 'MM/DD/YYYY HH24:MI:SS'),to_date('07/29/2015 14:11:23', 'MM/DD/YYYY HH24:MI:SS'),to_date('07/29/2015 14:11:23', 'MM/DD/YYYY HH24:MI:SS'),1,'','',0,,6,1,36,'','', );


---記錄用戶信息的USER$表
INSERT_INTO USER$_TABLE VALUES (36,'SCOTT',1,'5665B0DA34DDB8C5',4,3,to_date('12/09/2014 15:17:00', 'MM/DD/YYYY HH24:MI:SS'),to_date('06/29/2015 16:17:59', 'MM/DD/YYYY HH24:MI:SS'),to_date('06/30/2015 15:23:38', 'MM/DD/YYYY HH24:MI:SS'),,0,'',1,,,0,0,'DEFAULT_CONSUMER_GROUP','',0,,,'S:23DCE06BB6C5408756DFE8FE2C307CC5E5BCA05B9BE6C06AE53FA9AAD21C','', );


---記錄列信息的COL$表
INSERT_INTO COL$_TABLE VALUES (41415,1,1,22,0,'ID',2,22,0,,,0,,'',1,0,0,0,0,0,0,'','', );
INSERT_INTO COL$_TABLE VALUES (41415,2,2,10,0,'C2',1,10,0,,,0,,'',2,0,852,1,0,0,10,'','', );


---記錄表信息的TAB$表
INSERT_INTO TAB$_TABLE VALUES (41415,41415,41,9,146,,,2,,10,40,1,255,1073741825,'--------------------------------------',,,,,,,,,,,,,2,2,536870912,0,0,736,,,'','',to_date('07/29/2015 06:11:23', 'MM/DD/YYYY HH24:MI:SS'));


該有的信息都有了,在目標庫解析archivelog的時候參考上面的四組信息至少可以進行如下轉換
"OBJ# 41415"=>T0729_2
"COL 1"=>'ID'
"COL 2"=>'C2'


至於table_owner如何從"UNKNOWN"=>SCOTT,猜測原理和上面一樣,archived log內部也準確記錄了userid,只不過沒有直接顯示出user_id而是用UNKNOWN替代了。


爲了避免logminer字典文件被誤執行,dictionary 文件裏故意用insert_into替代insert,並且在每個表名後增加了_TABLE,比如COL$被表示成COL$_TABLE。
因此logminer dictionary裏的語句稍加修改後就能在數據庫上執行,但要注意這些insert語句如果在目標庫上執行(不同於生成redolog file的數據庫),即使執行成功,也不能在目標庫上使用DICT_FROM_ONLINE_CATALOG方式解析redolog裏的sql語句,如果這樣做會受到如下報錯:
SQL> exec DBMS_LOGMNR.START_LOGMNR(DictFileName=>'',Options=>dbms_logmnr.DICT_FROM_ONLINE_CATALOG);


*
ERROR at line 1:
ORA-01295: DB_ID mismatch between dictionary USE_ONLINE_CATALOG and logfiles
ORA-06512: at "SYS.DBMS_LOGMNR", line 58
ORA-06512: at line 1


因爲Oracle在使用online catalog也就是本機的數據字典解析時,會檢測被解析的redolog file裏的dbid和目標庫的dbid是否一致,如果不一致就會報出上面的錯誤。


文本方式保存logminer dictionary的可移植性較強,但存在的問題是dictionary file可能被惡意篡改,比如我們將tstdb1.dic裏的T0729_2批量替換成AAAA
那麼解析出來的SQL裏顯示的就是'AAAA'


exec dbms_logmnr.add_logfile(logfilename=>'/oradata04/o1_mf_1_109_1l8oUn0kl_.arc',options=>DBMS_LOGMNR.NEW);


exec DBMS_LOGMNR.START_LOGMNR(DictFileName=>'/oradata04/tstdb1.dic',Options=>0);   <---DictFileName指定了dictionary文件的路徑


set linesize 120
set linesize 120 pagesize 140
select sql_redo,sql_undo from V$LOGMNR_CONTENTS;
SQL_REDO
------------------------------------------------------------------------------------------------------------------------
SQL_UNDO
------------------------------------------------------------------------------------------------------------------------
。。。。省略部分輸出


insert into "SCOTT"."AAAA"("ID","C2") values ('1','a');
delete from "SCOTT"."AAAA" where "ID" = '1' and "C2" = 'a' and ROWID = 'AAAKHHAAJAAAACVAAA';


insert into "SCOTT"."AAAA"("ID","C2") values ('2','b');
delete from "SCOTT"."AAAA" where "ID" = '2' and "C2" = 'b' and ROWID = 'AAAKHHAAJAAAACVAAB';


commit;


如果我們選擇redolog file作爲logminer dictionary的載體,就不存在這個問題


(2)把logminer dictionary構建在redolog file的情況
###在tstdb1庫上構造logminer dictionary
***tstdb1庫上手工切換一個logfile後,current redolog變爲了Sequence#  142
alter system switch logfile;


SYS@tstdb1-SQL> archive log list
Database log mode              Archive Mode
Automatic archival             Enabled
Archive destination            USE_DB_RECOVERY_FILE_DEST
Oldest online log sequence     140
Next log sequence to archive   142
Current log sequence           142
SYS@tstdb1-SQL> 


***執行update
update t0729_3 set username='NEWUSER1' where user_id<30;
commit;


***將logminer dictionary build到redolog裏
SYS@tstdb1-SQL> exec DBMS_LOGMNR_D.BUILD(dictionary_filename=>NULL,dictionary_location=>NULL,options=>dbms_logmnr_d.STORE_IN_REDO_LOGS);


PL/SQL procedure successfully completed.


***build完成後發現當前的redolog sequence已經前進到了144,說明142、143已經被歸檔
SYS@tstdb1-SQL> archive log list
Database log mode              Archive Mode
Automatic archival             Enabled
Archive destination            USE_DB_RECOVERY_FILE_DEST
Oldest online log sequence     142
Next log sequence to archive   144
Current log sequence           144


通過v$archivelog可以看出哪些歸檔日誌包含logminer dictionary
col name format a90
set linesize 150
select name,ARCHIVED,DICTIONARY_BEGIN,DICTIONARY_END from v$archived_log where name like '%o1_mf_1_14%_.arc';


NAME                                                                                       ARC DIC DIC
------------------------------------------------------------------------------------------ --- --- ---
/oradata06/fra/TSTDB1/archivelog/2015_08_05/o1_mf_1_140_1lGtfKCjT_.arc                     YES NO  NO
/oradata06/fra/TSTDB1/archivelog/2015_08_05/o1_mf_1_141_1lHQRCI8J_.arc                     YES NO  NO
/oradata06/fra/TSTDB1/archivelog/2015_08_05/o1_mf_1_142_1lHQpRoEe_.arc                     YES NO  NO
/oradata06/fra/TSTDB1/archivelog/2015_08_05/o1_mf_1_143_1lHQpcQR8_.arc                     YES YES YES    <---sequence# 143含有logminer dictionary


###在shzw庫上解析出tstdb1庫上執行的update語句
***先把o1_mf_1_142_1lHQpRoEe_arc、.o1_mf_1_143_1lHQpcQR8_.arc兩個archivelog複製到shzw庫所在主機
scp /oradata06/fra/TSTDB1/archivelog/2015_08_05/o1_mf_1_142_1lHQpRoEe_.arc [email protected]:/oradata04/
scp /oradata06/fra/TSTDB1/archivelog/2015_08_05/o1_mf_1_143_1lHQpcQR8_.arc [email protected]:/oradata04/


剛纔我們的update操作生成在sequence# 142這個archivelog裏,但是如果要正確解析出這條update語句,必須將sequence# 143也加入進來


exec dbms_logmnr.add_logfile(logfilename=>'/oradata04/o1_mf_1_142_1lHQpRoEe_.arc',options=>DBMS_LOGMNR.NEW);


exec dbms_logmnr.add_logfile(logfilename=>'/oradata04/o1_mf_1_143_1lHQpcQR8_.arc',options=>DBMS_LOGMNR.ADDFILE);   <---加入sequence# 143


exec DBMS_LOGMNR.START_LOGMNR(DictFileName=>'',Options=>DBMS_LOGMNR.DICT_FROM_REDO_LOGS);


***解析出了操作T0729_3的update語句
SQL> SQL> set linesize 120
SQL> set linesize 120 pagesize 140
SQL> select seg_name,sql_redo,sql_undo from V$LOGMNR_CONTENTS where seg_name='T0729_3';


SEG_NAME
------------------------------------------------------------------------------------------------------------------------
SQL_REDO
------------------------------------------------------------------------------------------------------------------------
SQL_UNDO
------------------------------------------------------------------------------------------------------------------------
T0729_3
update "SYS"."T0729_3" set "USERNAME" = 'NEWUSER1' where "USERNAME" = 'DIP' and "USER_ID" = '14' and "CREATED" = TO_DATE
('20141110 21:18:04', 'yyyymmdd hh24:mi:ss') and ROWID = 'AAAKH8AAHAAAACDAAB';
update "SYS"."T0729_3" set "USERNAME" = 'DIP' where "USERNAME" = 'NEWUSER1' and "USER_ID" = '14' and "CREATED" = TO_DATE
('20141110 21:18:04', 'yyyymmdd hh24:mi:ss') and ROWID = 'AAAKH8AAHAAAACDAAB';


T0729_3
update "SYS"."T0729_3" set "USERNAME" = 'NEWUSER1' where "USERNAME" = 'ORACLE_OCM' and "USER_ID" = '21' and "CREATED" =
TO_DATE('20141110 21:18:49', 'yyyymmdd hh24:mi:ss') and ROWID = 'AAAKH8AAHAAAACDAAC';
update "SYS"."T0729_3" set "USERNAME" = 'ORACLE_OCM' where "USERNAME" = 'NEWUSER1' and "USER_ID" = '21' and "CREATED" =
TO_DATE('20141110 21:18:49', 'yyyymmdd hh24:mi:ss') and ROWID = 'AAAKH8AAHAAAACDAAC';


T0729_3
update "SYS"."T0729_3" set "USERNAME" = 'NEWUSER1' where "USERNAME" = 'SYS' and "USER_ID" = '0' and "CREATED" = TO_DATE(
'20141110 21:16:12', 'yyyymmdd hh24:mi:ss') and ROWID = 'AAAKH8AAHAAAACDAAQ';
update "SYS"."T0729_3" set "USERNAME" = 'SYS' where "USERNAME" = 'NEWUSER1' and "USER_ID" = '0' and "CREATED" = TO_DATE(
'20141110 21:16:12', 'yyyymmdd hh24:mi:ss') and ROWID = 'AAAKH8AAHAAAACDAAQ';


T0729_3
update "SYS"."T0729_3" set "USERNAME" = 'NEWUSER1' where "USERNAME" = 'SYSTEM' and "USER_ID" = '5' and "CREATED" = TO_DA
TE('20141110 21:16:12', 'yyyymmdd hh24:mi:ss') and ROWID = 'AAAKH8AAHAAAACDAAR';
update "SYS"."T0729_3" set "USERNAME" = 'SYSTEM' where "USERNAME" = 'NEWUSER1' and "USER_ID" = '5' and "CREATED" = TO_DA
TE('20141110 21:16:12', 'yyyymmdd hh24:mi:ss') and ROWID = 'AAAKH8AAHAAAACDAAR';


T0729_3
update "SYS"."T0729_3" set "USERNAME" = 'NEWUSER1' where "USERNAME" = 'OUTLN' and "USER_ID" = '9' and "CREATED" = TO_DAT
E('20141110 21:16:14', 'yyyymmdd hh24:mi:ss') and ROWID = 'AAAKH8AAHAAAACDAAS';
update "SYS"."T0729_3" set "USERNAME" = 'OUTLN' where "USERNAME" = 'NEWUSER1' and "USER_ID" = '9' and "CREATED" = TO_DAT
E('20141110 21:16:14', 'yyyymmdd hh24:mi:ss') and ROWID = 'AAAKH8AAHAAAACDAAS';

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章