我正在开发一个基于LogMiner的解决方案来捕获更改,并且在尝试挖掘与CLOB或BLOB操作相关的重做事件时,我发现了一组不寻常的期望。
在我的用例中,我已经将记录插入到一个包含3个CLOB字段的表中,其中一个CLOB域的值很小,而另外两个CLOB场必须使用LOB_WRITE操作进行设置。
当我设置一个在事务提交之前开始并在事务提交之后结束的起始LogMiner SCN范围时,我会在V$LOGMNR_CONTENTS
中得到完整的预期行,它们是:
0a00070084220000 37717288 START
0a00070084220000 37717288 INSERT
0a00070084220000 37717312 SEL_LOB_LOCATOR
0a00070084220000 37717312 LOB_WRITE (several of these as expected)
0a00070084220000 37717331 SEL_LOB_LOCATOR
0a00070084220000 37717331 LOB_WRITE (several of these as expected)
0a00070084220000 37717332 INSERT (sets the smaller clob data values)
0a00070084220000 37717334 COMMIT
当使用特定的开始/结束SCN范围启动挖掘会话时,会出现异常位。
例如,当我从37717239挖掘到37717289时,我希望LogMiner在表中同时提供START
和INSERT
;然而仅存在CCD_ 4操作。
此外,当我从37717290挖掘到37717340时,我希望LogMiner提供所有的SEL_LOB_LOCATOR
、LOB_WRITE
以及随后的INSERT
和COMMIT
;而只有随后的CCD_ 9和CCD_。
我能从中得出的唯一断言是,当你拆分一个transaction时,LogMiner似乎遇到了麻烦,在这个transaction中,某些重做事件代表了与LOB操作相关的各种合成操作,因此,我能够真正始终重建一系列事件的唯一方法是从37717288开始挖掘,以迫使LogMiner事务在具体化内容视图中的行时可用。
为什么LogMiner会这样做?为什么在使用我上面介绍的SCN范围拆分事务时,它没有正确实现?
对于Logminer,任何单个命令都是原子命令。在这种情况下,它开始于37717288,结束于37717332。它不能拆分。如果你会询问任何分割它的范围,Logminer不会故意提取它(所以你不会得到单个命令的部分结果(。
这也适用于大型非LOB命令,如生成许多内部命令的DDL(例如更改表修改列默认值(
此外,请注意,从Logminer中获取LOB的值是不可靠的。只要玩弄价值观,你就会发现它是高度不一致的。(我有一些测试可以证明这一点,所以如果你感兴趣,我可以提供(。
测试如下:定义一个有2个lob的表,创建2行——第一行有一个lob,第二行有两个lob。
drop table sample1.clobs2;
create table sample1.clobs2 (id number not null, clob1 clob not null, clob2 clob);
--start
select current_scn from v$database;
insert into sample1.clobs2 (id, clob1) values (3, 'abc');
insert into sample1.clobs2 (id, clob1, clob2) values (4, 'abc', '2abc');
commit;
update sample1.clobs2 set clob1='def' where id=3;
update sample1.clobs2 set clob1='def', clob2='2def' where id=4;
commit;
update sample1.clobs2 set clob1=rpad('ghj',30000,'Z') where id=3;
update sample1.clobs2 set clob1=rpad('ghj',30000,'Z'), clob2=rpad('ghj',30000,'Z') where id=4;
commit;
--end
select current_scn from v$database;
启动日志矿工:
exec DBMS_LOGMNR.end_LOGMNR;
exec DBMS_LOGMNR.ADD_LOGFILE('put here any logfile(select MEMBER from v$logfile), logminer will do the rest');
begin DBMS_LOGMNR.START_LOGMNR(
STARTSCN => put here the scn from the above test,
ENDSCN => put here the scn from the above test,
OPTIONS => -- I leave all the possible parameters here just for you to play
--DBMS_LOGMNR.DICT_FROM_REDO_LOGS +
--DBMS_LOGMNR.DICT_FROM_ONLINE_CATALOG +
DBMS_LOGMNR.CONTINUOUS_MINE +
--DBMS_LOGMNR.COMMITTED_DATA_ONLY+
--DBMS_LOGMNR.DDL_DICT_TRACKING+
DBMS_LOGMNR.NO_ROWID_IN_STMT+
DBMS_LOGMNR.NO_SQL_DELIMITER
);
end;
/
检查结果:
select scn, (XIDUSN || '.' || XIDSLT || '.' || XIDSQN) AS transaction_id, operation, seg_name, ROW_ID, rollback, csf,SQL_REDO, c.*
from v$logmnr_contents c
where 1=1
(seg_name='OBJ# put here the object id of the table sample1.clobs2' or operation='COMMIT');
您将看到:
- 大lob与小lob的行为不同
- 如果只更新了一个lob,则无法理解它是哪一个(第一个还是第二个(
此外,这种行为会在不同的Oracle版本之间发生变化。