转。
Oracle补全日志(Supplemental logging)特性因其作用的不同可分为以下几种:最小(Minimal),支持所有字段(all),支持主键(primary key),支持唯一键(unique),支持外键(foreign key)。包括LONG,LOB,LONG RAW及集合等字段类型均无法利用补全日志。
最小(Minimal)补全日志开启后可以使得logmnr工具支持链式行,簇表和索引组织表。可以通过以下SQL检查最小补全日志是否已经开启:
SELECT supplemental_log_data_min FROM v$database; |
若结果返回YES或IMPLICIT则说明已开启最小补全日志,当使用ALL,PRIMARY,UNIQUE或FOREIGN补全日志时最小补全日志默认开启(即检查结果为IMPLICIT)。
一般情况下我们在使用逻辑备库时启用主键和惟一键的补全日志,而有时表上可能没有主键,惟一键或唯一索引;我们通过以下实验总结这种情况下Oracle的表现。
首先建立相关的测试表:
alter database add supplemental log data (primary key,unique index) columns ;
create table test (t1 int , t2 int ,t3 int ,t4 int ); alter table test add constraint pk_t1 primary key (t1); --添加主键 随后使用循环插入一定量的数据 update test set t2=10; commit; -- 更新数据 |
使用LOGMNR工具分析之前的操作,可以看到REDO中记录的SQL形式如下:
update "SYS"."TEST" set "T2" = '10' where "T1" = '64' and "T2" = '65' and ROWID = 'AAAMiSAABAAAOhiAA/'; |
其中where字句后分别记录了主键值,被修改字段的值和原行的ROWID。
现在我们将原表上的主键去掉来观察。
alter table test drop constraint pk_t1 ;
update test set t2=11; commit; -- 更新数据 使用LOGMNR分析可以发现,REDO中的SQL记录如下: update "SYS"."TEST" set "T2" = '11' where "T1" = '1' and "T2" = '10' and "T3" = '3' and "T4" = '4' and ROWID = 'AAAMiSAABAAAOhiAAA'; |
当没有主键的情况下,where子句后记录了所有列值和ROWID。
以下实验在存在唯一索引情况下的表现
create unique index pk_t1 on test(t1); update test set t2=15; commit; 使用LOGMNR分析可以发现,REDO中的SQL记录如下: update "SYS"."TEST" set "T2" = '15' where "T1" = '9' and "T2" = '11' and "T3" = '11' and "T4" = '12' and ROWID = 'AAAMiSAABAAAOhiAAI'; 以上是t1列有唯一索引但不限定not null的情况,下面我们加上not null限制 alter table test modify t1 not null; update test set t2=21; commit; 使用LOGMNR分析可以发现,REDO中的SQL记录如下: update "SYS"."TEST" set "T2" = '21' where "T1" = '2' and "T2" = '15' and ROWID = 'AAAMiSAABAAAOhiAAB'; |
如以上SQL所示,在存在唯一索引的情况下where子句后仍记录了所有列和ROWID;在存在唯一索引和非空约束的情况下表现与存在主键的情况一致。
当某个表上的列数量较多时且没有主键或唯一索引和非空约束的情况下,开启补全日志可能导致重做日志总量大幅提高。
首先建立一个存在250列的表:
Drop table test; create table test ( t1 varchar2(5), t2 varchar2(5), t3 varchar2(5), t4 varchar2(5), …t250 varchar2(5))
insert into test values ('TEST','TEST' ……); commit; --将255个列填入数据 alter database drop supplemental log data (primary key,unique index) columns; --关闭补全日志 set autotrace on; update test set t2='BZZZZ' where t1='TEST'; commit; 可以从自动跟踪信息中看到,本条更新产生了516的重做量。 alter database add supplemental log data (primary key,unique index) columns; --重新开启补全日志 update test set t2='FSDSD' where t1='TEST'; 跟踪信息显示产生了3044的重做量。 |
补全日志因作用域的不同又可分为数据库级的和表级的。表级补全日志又可以分为有条件的和无条件的。有条件限制的表级补全日志仅在特定列被更新时才会起作用,有条件限制的表级补全日志较少使用,这里我们不做讨论。
下面我们来观察无条件限制表级补全日志的具体表现:
alter database drop supplemental log data (primary key,unique index) columns;
alter table test add supplemental log data (primary key,unique index) columns; update test set t2='ZZZZZ'; commit; 使用LOGMNR工具查看redo中的SQL: 可以发现where子句之后包含了所有列值。 delete test; commit; 使用LOGMNR工具查看redo中的SQL: delete from "SYS"."TEST" where "T1" = 'TEST' and "T2" = 'ZZZZZ' and "T3" = 'TEST' and "T4" = 'TEST' and "T5" …… delete操作同样在where子句之后包含了所有列值。 又我们可以针对表上字段建立特定的补全日志组,以减少where子句后列值的出现。 alter table test drop supplemental log data (primary key,unique index) columns; --关闭表上原先的补全日志 alter table test add supplemental log group test_lgp (t1 ,t2,t3,t4,t5,t6,t12,t250) always; --创建补全日志组 update test set t2='XXXXX' ; commit; 使用LOGMNR工具查看redo中的SQL: update "SYS"."TEST" set "T2" = 'XXXXX' where "T1" = 'TEST' and "T2" = 'TEST' and "T3" = 'TEST' and "T4" = 'TEST' and "T5" = 'TEST' and "T6" = 'TEST' and "T12" = 'TEST' and "T250" = 'TEST' and ROWID = 'AAAMieAABAAAOhnAAA'; 如上所示重做日志中正确地显示了UPDATE操作中用户指定的字段值。 delete test; 使用LOGMNR工具查看redo中的SQL: delete from "SYS"."TEST" where "T1" = 'TEST' and "T2" = 'XXXXX' and "T3" = 'TEST' …… delete操作在重做日志中仍然保留了所有列值。 |
针对字段较多的表,我们在能够以多个列保证数据唯一性且非空的情况下(即应用概念上的主键)来指定表上的补全日志组,以减少update操作时所产生的重做日志,而对于delete操作则无法有效改善。