- 最后登录
- 2017-5-4
- 在线时间
- 81 小时
- 威望
- 999
- 金钱
- 2391
- 注册时间
- 2013-9-11
- 阅读权限
- 150
- 帖子
- 1124
- 精华
- 5
- 积分
- 999
- UID
- 1220
|
1#
发表于 2013-9-14 23:09:33
|
查看: 4785 |
回复: 1
有些客户需要记录关键表(源表)的修改历史,即,将源表什么数据何时做了什么修改记录到历史表,以便将来恢复、审计、追踪或作为ETL的源。历史表也称为跟踪表、审计表、historical data或transaction-history table。
传统实现方法是打开源表DB审计或在源表上建立insert, update和delete触发器,审计或触发器将DML历史记录到历史表,缺点是影响生产系统,审计或触发器开销大,生产系统不得不添加历史表,而且需要开发。有没有不影响甚产系统,又不需要开发的方法呢?有,就是用GoldenGate的参数InsertAllRecords实现。
历史表通常大致结构是:
源数据,
DML时间,
transaction序号,
DML在transaction中的顺序,
DML类型,
数据是update before image还是after image。
我们以GoldenGate Oracle -> Oracle复制来介绍实现方法。
源端我们建立两张源表:
源表有PK
create table t_pk (
n number primary key,
c char(10),
v varchar2(10)
);
源表无PK
create table t_no_pk (
c char(10),
v varchar2(10)
);
目标端端我们建立两张历史表:
源表有PK对应的历史表
create table t_pk_history (
n number,
c char(10),
v varchar2(10),
COMMITTIMESTAMP timestamp,
CSN number,
seq number,
OPTYPE varchar2(20),
BEFOREAFTERINDICATOR char(6)
);
源表无PK对应的历史表
create table t_no_pk_history (
c char(10),
v varchar2(10),
COMMITTIMESTAMP timestamp,
CSN number,
seq number,
OPTYPE varchar2(20),
BEFOREAFTERINDICATOR char(6)
);
源端
GGSCI > Add TranData scott.t_pk 此命令错误,见下面解释。
GGSCI > Add TranData scott.t_no_pk
t_no_pk没有PK,GG会把所有字段的SUPPLEMENTAL LOG打开。
t_pk有PK,GG只把PK字段n的SUPPLEMENTAL LOG打开,这会加速一般复制,却对update语句在历史表中的表现有影响,即,当修改一部分非PK字段时,例如,两个非PK字段v和c,只修改字段v时,Oracle DB Log只记PK字段n和字段v的修改,不记字段c,这就造成历史表中字段c的before image和after image都是NULL,我们在看历史表时,就不容易确定字段c的NULL是原有的呢,还是没有更新字段c造成的。为了解决这个问题,熟悉GoldenGate的人会想到Extract参数NoCompressUpdates,不幸的是这参数不支持Oracle DB,见《Reference Guide》,不过为了保险,我还是把它写到Extract参数文件里。天无绝人之路,下面命令打开所有字段的SUPPLEMENTAL LOG后,就能保证没有被修改的非PK字段before image和after image记录入历史表:
GGSCI > Add TranData scott.t_pk, Cols (n, c, v), NoKey
上面命令也可以这么写:
GGSCI > Add TranData scott.t_pk, Cols (c, v)
你会发现Cols (c, v)中没有PK字段n,不用担心,GoldenGate会自动把PK字段n加进去。
源端Extract参数exta.prm:
Extract extA
Userid goldengate, Password goldengate
RmtHost 192.168.19.133, MgrPort 7809
RmtTrail ./dirdat/ra
NoCompressDeletes
NoCompressUpdates
GetUpdateBefores
Table scott.*;
参数说明:
NoCompressDeletes:无论所有字段的SUPPLEMENTAL LOG是否打开,默认有PK的表在delete时只记录PK字段的值,造成历史表中其他字段都记为NULL,有时我们希望知道被删除的所有字段的值,解决办法是参数Extract添加参数NoCompressDeletes,这参数支持Oracle DB。
NoCompressUpdates:支持DB2 LUW and DB2 z/OS, Teradata version 12 or later, SQL Server, Sybase,不支持Oracle DB。实际上,此参数对Oracle DB有时有效有时无效,加上这个参数只是为了保险和通用。
GetUpdateBefores:把Update的before image也写入trail file。如果不加这个参数,则只有after imag写入trail file,这会加速一般复制,却造成历史表无法记录before image。
目标端Replicat参数repa.prm:
Replicat repa
UserId goldengate, Password goldengate
AssumeTargetDefs
DiscardFile ./dirrpt/repa.dsc, Purge
GetUpdateBefores
Map scott.t_pk, Target scott.t_pk_history, &
ColMap ( UseDefaults , &
COMMITTIMESTAMP = @Date ( "YYYY-MM-DD HH:MI:SS.FFFFFF", "YYYY-MM-DD HH:MI:SS.FFFFFF", @GetEnv ( "GGHEADER", "COMMITTIMESTAMP" ) ), &
CSN = @GetEnv ( "TRANSACTION", "CSN" ), &
seq = @Compute ( @GetEnv ( "GGHEADER", "LOGRBA" ) * 10000000000 + @GetEnv ( "GGHEADER", "LOGPOSITION" ) ) , &
OPTYPE = @GetEnv ( "GGHEADER", "OPTYPE" ), &
BEFOREAFTERINDICATOR = @GetEnv ( "GGHEADER", "BEFOREAFTERINDICATOR" ) &
) , InsertAllRecords ;
Map scott.t_no_pk, Target scott.t_no_pk_history, &
ColMap ( UseDefaults , &
COMMITTIMESTAMP = @Date ( "YYYY-MM-DD HH:MI:SS.FFFFFF", "YYYY-MM-DD HH:MI:SS.FFFFFF", @GetEnv ( "GGHEADER", "COMMITTIMESTAMP" ) ), &
CSN = @GetEnv ( "TRANSACTION", "CSN" ), &
seq = @Compute ( @GetEnv ( "GGHEADER", "LOGRBA" ) * 10000000000 + @GetEnv ( "GGHEADER", "LOGPOSITION" ) ) , &
OPTYPE = @GetEnv ( "GGHEADER", "OPTYPE" ), &
BEFOREAFTERINDICATOR = @GetEnv ( "GGHEADER", "BEFOREAFTERINDICATOR" ) &
) , InsertAllRecords ;
参数说明:
UseDefaults:历史表前面的字段和源表一样。
InsertAllRecords:把update转换成before image和after image插入历史表;delete转换成被删记录插入历史表。
字段说明:
COMMITTIMESTAMP:DML所属transaction提交的时间。
CSN:DML所属transaction的SCN。
seq:某条DML在事务中顺序无法直接获得,只能用间接方法,将LOGRBA(源端DB Log #)和LOGPOSITION(DML在源端DB Log中的偏移量)合并,合并结果由小到大就表示某条DML在事务中顺序。假设源端最大的DB Log <= 10,000,000,000 byte (10GB),则计算表达式如下:
seq = LOGRBA * 10000000000 + LOGPOSITION
上面数值表达式不用直接用于ColMap,必须放到函数@Compute()中计算。
您可能注意到了,参数文件中没有GetTruncates,因为GetTruncates会造成truncate源表时,也truncate历史表,我还没有找到把truncate转成insert的办法。:(
下面开始试验。
设置SQL*Plus显式格式:
SQL> set pagesize 9999
SQL> set linesize 150
SQL> column n format 99
SQL> column c format a4
SQL> column v format a4
SQL> alter session set NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF';
SQL> column COMMITTIMESTAMP format a26
SQL> column SEQ for 999999999999
各种情况排列组合试验:
1) 源表有PK
1.1) insert
insert into t_pk values (1, 'a', 'a');
insert into t_pk values (2, 'b', 'b');
commit;
SQL> select * from t_pk_history order by COMMITTIMESTAMP;
N C V COMMITTIMESTAMP CSN SEQ OPTYPE BEFORE
--- ---- ---- -------------------- ---------- ------------- -------------------- ------
1 a a 2011-05-31 12:41:34 1300233 280011160452 INSERT AFTER
2 b b 2011-05-31 12:41:34 1300233 280011161236 INSERT AFTER
|
|