日志文件的块大小查询

日志文件的块大小可以通过如下方法来查询:

select distinct block_size from v$archived_log;

清理脏块

将所有的脏块给清到磁盘上

alter system flush buffer_cache

查询日志分组信息

select * from v$log;

查询当前日志分组

select * from v$log where status='CURREN';

创建日志分析存储过程以及查询常见信息

# 加载字典至文件 /u01/app/logminer/dictionary.ora,需要目录和文件的所有者所数组是 oracle:dba
exec dbms_logmnr_d.build( 'dictionary.ora', '/u01/app/logminer'); # 可选

# 加载日志
exec dbms_logmnr.add_logfile('/usr/lib/oracle/xe/app/oracle/flash_recovery_area/XE/onlinelog/o1_mf_1_djfw9s0s_.log', dbms_logmnr.NEW);

# 再加载日志,可选
exec dbms_logmnr.add_logfile('/usr/lib/oracle/xe/app/oracle/flash_recovery_area/XE/onlinelog/o1_mf_1_djfw9s0s_.log', dbms_logmnr.ADDFILE);

# 启动logminer
dbms_logmnr.start_logmnr(DictFileName=>'/u01/app/logminer/dictionary.ora');

至此,存储已经创建好,根据需要,字典可以选择加载或不加载,一般在是在执行DDL之后需要重新加载字典,接下来查询常见信息

select SCN,INFO,to_char(timestamp,'yyyy-mm-dd hh24:mi:ss'),table_name,OPERATION,SQL_REDO,SQL_UNDO,status from v$logmnr_contents;

测试记录

只用 ID 作为条件且只更新 LOB 字段(SQL_REDO 和 SQL_UNDO 中缺失 条件)

无主键表

create table event
(
 id number,
 name varchar2(20),
 a_blob blob,
 a_clob  clob
);

insert into event (id,name,a_blob,a_clob) values(1,'11','01010101010101','test-clob');

update event set a_blob = null where id =3;

## where 后面的条件缺失
----- sql_redo = update "SYSTEM"."EVENT" set "A_BLOB" = NULL where ;
----- sql_undo = update "SYSTEM"."EVENT" set "A_BLOB" = NULL where ;

有主键表

create table event
(
 id number primary key,
 name varchar2(20),
 a_blob blob,
 a_clob  clob

);


insert into event (id,name,a_blob,a_clob) values(1,'11','01010101010101','test-clob');

update "SYSTEM"."EVENT" set "A_BLOB" = NULL where ;

### where 后面的条件缺失
-----> sql_redo = update "SYSTEM"."EVENT" set "A_BLOB" = NULL where ;
-----> sql_undo = update "SYSTEM"."EVENT" set "A_BLOB" = NULL where ;

综上,可以排除未设主键的原因

update event set a_blob = null,name = 'xx' where id =1;

--sql_redo :  update "SYSTEM"."EVENT" set "NAME" = 'xx' where "NAME" = '11';
--sql_redo :  update "SYSTEM"."EVENT" set "A_BLOB" = NULL where "NAME" = 'xx';

--sql_undo :  update "SYSTEM"."EVENT" set "NAME" = '11' where "NAME" = 'xx';
--sql_undo :  update "SYSTEM"."EVENT" set "A_BLOB" = NULL where "NAME" = 'xx';

条件中虽然 ID 字段缺失,但是被更新的字段除了 Lob 之外其对于的旧值出现在 where 子句中,有个猜想,如下测试:

update event set a_blob = null,id = 1 where id =1;

--sql_redo :  update "SYSTEM"."EVENT" set "ID" = '1', "A_BLOB" = EMPTY_BLOB() where "ID" = '1';
--sql_redo : update "SYSTEM"."EVENT" set "A_BLOB" = NULL where "ID" = '1';

--sql_undo :  update "SYSTEM"."EVENT" set "ID" = '1', "A_BLOB" = NULL where "ID" = '1';
--sql_undo :  update "SYSTEM"."EVENT" set "A_BLOB" = NULL where "ID" = '1'

再如:

update event set a_blob = null,name='bbb',id = 1 where id =1;

--sql_redo :  update "SYSTEM"."EVENT" set "ID" = '1', "NAME" = 'bbb', "A_BLOB" = EMPTY_BLOB() where "ID" = '1' and "NAME" = 'aaa';
--sql_redo :  update "SYSTEM"."EVENT" set "A_BLOB" = NULL where "ID" = '1' and "NAME" = 'bbb';

--sql_undo :  update "SYSTEM"."EVENT" set "ID" = '1', "NAME" = 'aaa', "A_BLOB" = NULL where "ID" = '1' and "NAME" = 'bbb';
--sql_undo :  update "SYSTEM"."EVENT" set "A_BLOB" = NULL where "ID" = '1' and "NAME" = 'bbb';

通过测试发现,除了 ID 字段,所有被修改的非 LOB 字段的旧值都会出现在 SQL_REDO 和 SQL_UNDO 的 where 子句中。 为了避免在单独的以 ID 作为标识修改 LOB 字段时导致的在 SQL_REDO 和 SQL_UNDO 中条件缺失的问题,应该在 set 子句 中指定 ID 字段的值,尽管 ID 字段并没有修改

update event set a_blob = null,name = '1-1',id = 1 where id =1;

  ## 注意此处的 EMPTY_BLOB()
 --sql_redo :  update "SYSTEM"."EVENT" set "ID" = '1', "NAME" = '1-1', "A_BLOB" = EMPTY_BLOB() where "ID" = '1' and "NAME" = 'xx';
 --sql_redo :  update "SYSTEM"."EVENT" set "A_BLOB" = NULL where "ID" = '1' and "NAME" = '1-1';

 --sql_undo :  update "SYSTEM"."EVENT" set "ID" = '1', "NAME" = 'xx', "A_BLOB" = NULL where "ID" = '1' and "NAME" = '1-1';
 --sql_undo :  update "SYSTEM"."EVENT" set "A_BLOB" = NULL where "ID" = '1' and "NAME" = '1-1';
update event set a_blob = null,a_clob = null ,name = '1-1',id = 1 where id =1;

  ## 注意此处的 EMPTY_BLOB()
--sql_redo :  update "SYSTEM"."EVENT" set "ID" = '1', "NAME" = '1-1', "A_BLOB" = EMPTY_BLOB() where "ID" = '1' and "NAME" = '1-1';
--sql_redo :  update "SYSTEM"."EVENT" set "A_BLOB" = NULL, "A_CLOB" = NULL where "ID" = '1' and "NAME" = '1-1';

--sql_undo :  update "SYSTEM"."EVENT" set "ID" = '1', "NAME" = '1-1', "A_BLOB" = NULL where "ID" = '1' and "NAME" = '1-1';
--sql_undo :  update "SYSTEM"."EVENT" set "A_BLOB" = NULL, "A_CLOB" = NULL where "ID" = '1' and "NAME" = '1-1';

更新BLOB字段内容(BLOB字段无法通过 SQL_UNDO 还原)


>表中 id 为 1 的记录字段 a_blob 非空:insert into event (id,name,a_blob,a_clob) values(1,'11','01010101010101','test-clob');

update event set a_blob = '1010101',name = 'aaa' where id =1;

--sql_redo :  update "SYSTEM"."EVENT" set "NAME" = 'aaa', "A_BLOB" = EMPTY_BLOB() where "NAME" = '1-1';
--sql_redo :  update "SYSTEM"."EVENT" set "A_BLOB" = HEXTORAW('01010101') where "NAME" = 'aaa';

--sql_undo :  update "SYSTEM"."EVENT" set "NAME" = '1-1', "A_BLOB" = NULL where "NAME" = 'aaa';
## 最终只是把 a_blob 的设置为 NULL,并不是原来的 '01010101010101'
--sql_undo :  update "SYSTEM"."EVENT" set "A_BLOB" = NULL where "NAME" = 'aaa';

如果使用 activeJDBC 形式 ,执行 update event set a_blob = 'read bytes[] from a file',name = 'aaa' where id =1;
日志中根本就没有出现 a_blob 字段
-- sql_undo :  update "SYSTEM"."EVENT" set "ID" = '1' where "ID" = '1';

sqldeveloper 和 jdbc 执行含 LOB 字段(超过4K)的 insert 对比

含 LOB 字段的 insert 操作,数据库均会先执行 insert 非 LOB 字段的内容,之后再 update LOB 字段的内容。 使用 sqldeveloper 界面 insert 含 LOB 字段的记录时,日志中的 insert 和 update 的 SCN 是相同的,而 使用 java 代码通过 activeJDBC insert 含 LOB 字段的记录时,日志中的 insert 和 update 的 SCN 是不 相同的。