
好久没研究Oracle了,今天碰到一个LogMiner的问题,特此记录一下。
数据库架构为:

此架构很简单,一个Oracle主库(简称A库),一个物理备库(简称B库),一个可读可写的Oracle库(简称C库),B和C在同一台机器上,这样的话,C库就可以通过LogMiner+平面文件字典的形式实时的解析B库的在线和归档日志,最后将解析出来的SQL通过简单转换,可以应用的其他的PG、MySQL、SQL Server等库。
开发反馈过来说,从B库解析出来的SQL包含如下的形式,delete from "SYS"."AABB" where ROWID = 'AAAXCBAAEAAAAIOAAA';,这类SQL不能转换为真实执行的SQL语句,可能会导致数据不一致的问题。
我起初以为是解析过程有问题,然后自己在出问题的数据库中捕捉了一下,发现的确有这种情况,一时之间有点蒙了。因为对于delete和insert语句来说,redo日志肯定会记录完整的字段内容,所以,这个肯定和附加日志是没有关系的。
接下来就是,在自己的环境中做了很多实验,发现不会只包含一个ROWID,都是这种情况:delete from "SYS"."AABB" where "ID" = '3' and ROWID = 'AAAUU4AABAAAc4DAAF';,且SQL_UNDO列都是有值的,而出现问题的删除语句的SQL_UNDO列是空的,这就比较奇怪咯。
最后只能搜索mos了,搜“v$logmnr_contents sql_redo only rowid”,结果发现第1篇就是Rollback Statement Does Not Contain Supplemental Log Data (Doc ID 462948.1),里边提到了,若执行了ROLLBACK,则会出现这种情况。于是我立马验证,发现的确如此。
那返回去想一下,为啥在生产环境验证的时候,一直有问题,而自己的环境却没有问题呢,因为:生产环境的SQL是客户这边给的,我没有细看,而我自己的环境的SQL是我之前整理过的SQL,因为SQL不能复制,故出现了这个问题。具体差异在哪里呢,在于我自己的SQL在解析的时候加了一个选项:options=>dbms_logmnr.committed_data_only ,这个表示只解析已提交的数据,问题就出现在这。
所以,排查完的结论就是,这种SQL不影响数据的完整的性,因为是主库回滚掉的,而解析库不能解析,那么也不会应用到其他库中。
完毕!
DROP TABLE AABB;
create table AABB(id int);
insert into "SYS"."AABB"("ID") values ('1');
commit;
begin
insert into "SYS"."AABB"("ID") values ('3');
rollback;
end;
SELECT' dbms_logmnr.add_logfile('''||MEMBER||''');'FROM v$logfile;
select*from v$archived_log WHERE sequence#>=678;
select*from v$archived_log
where to_date('2025-03-14 14:49:36','YYYY-MM-DD HH24:MI:SS') between first_time and next_time
and dest_id=1
orderby thread#;
BEGIN
dbms_logmnr.add_logfile('/tmp/o1_mf_1_680_mx7yt0hj_.arc');
dbms_logmnr.start_logmnr(options=>dbms_logmnr.dict_from_online_catalog);
-- dbms_logmnr.start_logmnr(options=>dbms_logmnr.dict_from_online_catalog + dbms_logmnr.committed_data_only);
-- dbms_logmnr.start_logmnr(dictfilename=>'/home/oracle/log.ora');
END;
SELECT scn, sql_redo, a.SQL_UNDO,timestamp,session_info,session#,serial#,ssn,rollback
FROM v$logmnr_contents a
WHERE a.TABLE_NAME =upper('AABB')
-- and a.operation='DELETE'
-- and a.ROW_ID = 'AAAYaiABfAABlvOAAc'
andtimestamp>= to_date('2025-03-14 10:20:00','YYYY-MM-DD HH24:MI:SS')
and rownum<=10
orderby scn,SSN;
begin
dbms_logmnr.end_logmnr();
end;
结果:

You use LogMiner to grab the information from Redo logs in source database (SUPPLEMENTAL LOGGING enabled for PK and UI) and apply this to target database. The view V$LOGMNR_CONTENTS doesn't show the complete syntax for SQL_REDO of the 'ROLLBACK' statements (including PK/UI, only ROWID is available, what is not applicable at target database).
connect /as sysdba
alter database add supplemental log data (all) columns;
connect test/test
droptable tab001;
purge recyclebin;
create table tab001 (col001 number);
alter table tab001 add constraint tab001_pk primary key (col001);
alter table tab001 add supplemental log data (primary key) columns;
alter table tab001 add supplemental log data (unique) columns;
insert into tab001 values (1);
rollback;When an application session is aborted (for instance it exists suddenly), a rollback should occur for any transaction failure. If a transaction is cancelled or fails, then Oracle needs to cleanup the uncommitted work that was done by this transaction so that other transactions can progress. This cleanup involves rolling back the uncommitted work. From LogMiner point of view, the rollback statement is reported by itself as SQL_REDO and not SQL_UNDO. For an SQL which rolls back, no undo SQL is generated, and the rollback flag is set. For an insert followed by a rollback the view V$LOGMNR_CONTENTS shows:
select scn,
to_char (timestamp, 'dd.mm.yy hh24:mi') timestamp,
rollback,
sql_redo,
sql_undo,
username,
operation
from v$logmnr_contents;
ForINSERT statement:
SCN TIMESTAMP ROLLBACK
---------- -------------- ----------
1330456 16.10.0713:50 0
SQL_REDO
---------------------------------------------------------------------------
insert into "TEST"."TAB001"("COL001") values ('1');
SQL_UNDO
---------------------------------------------------------------------------
deletefrom "TEST"."TAB001" where "COL001" ='1'and ROWID ='AAAM7WAAEAAAACFAAA';
USERNAME OPERATION
------------------------------ --------------------------------
TEST INSERT
ForROLLBACK statement:
SCN TIMESTAMP ROLLBACK
---------- -------------- ----------
1330456 16.10.0713:50 1
SQL_REDO
---------------------------------------------------------------------------
deletefrom "TEST"."TAB001" where ROWID ='AAAM7WAAEAAAACFAAA';
SQL_UNDO
---------------------------------------------------------------------------
USERNAME OPERATION
------------------------------ --------------------------------
TEST DELETE
==>For the "delete" statement reported as SQL_REDO , the ROLLBACKcolumnissetto1 which means that the delete was executed as part of a rollback operation.
There is no column or supplemental log data available in the redo generated by a rollback. This behaviour is covered in:
Bug 5921229 INCORRECT SQL_UNDO WITH LOGMINER AND SUPPLEMENTAL LOGGING ENABLED
and Enhancement Request:
Bug 6029733 INCLUDE INFORMATION ABOUT UNCOMMITED STATEMENTS IN LOGMINER OUTPUT
Use other Oracle supported methodologies (like Streams, Logical Standby) for maintaining a replica database.
1、navicat会自动提交事务,在navicat中,如下的的SQL
insert into "SYS"."AABB"("ID") values ('3');
rollback;和下面的SQL一样:
insert into "SYS"."AABB"("ID") values ('3');
commit;
rollback;需要特别注意。
2、挖掘日志的时候应该加上exec dbms_logmnr.start_logmnr(options=>dbms_logmnr.committed_data_only);,确保只查询提交后的数据。
3、redo日志也会记录rollback的信息,所以,若v$logmnr_contents视图的SQL_REDO列只有rowid信息没有附加日志信息,且SQL_UNDO为空,可以查看rollback列是否为1,若为1,则说明该语句已经被回滚了。
Rollback Statement Does Not Contain Supplemental Log Data (Doc ID 462948.1)