一【环境描述】
1、goldengate版本
./ggsci -v
Oracle GoldenGate Command Interpreter for Oracle
Version 11.2.1.0.4_02 15864821 OGGCORE_11.2.1.0.5_PLATFORMS_121214.0600_FBO
Linux, x64, 64bit (optimized), Oracle 11g on Dec 14 2012 11:41:04
2、database版本
sqlplus -v
SQL*Plus: Release 11.2.0.3.0 Production
3、表结构
备注:表无任何索引和约束,sxiaoxu表示源表,txiaoxu表示目标表.
代码语言:javascript复制create table sxiaoxu
(
id int not null,
name varchar2(50),
age int not null,
sex char(1)
)
create table txiaoxu
(
id int not null,
name varchar2(50),
age int not null,
sex char(1)
)
4、测试场景
4.1 构建完全相同数据,更新全部数据以及随机更新数据,查看目标端更新情况
4.2构造keycols相同的整条记录不相同,更新全部数据以及随机更新数据,查看目标端更新情况
4.3当表结构发生变化时,验证同步存在问题
4.4当表结构存在大字段类型时,更新数据,查看目标端更新情况
二【测试过程针对构建完全相同以及keycols相同的记录不同】
备注--构建完全相同数据,更新全部数据以及随机更新数据,查看目标端更新情况
--准备工作完成包括配置抽取进程,应用进程以及数据库级别附加日志、表附加日志
1、验证表附加日志是所有列
代码语言:javascript复制info trandata XIAOXU.SXIAOXU
Logging of supplemental redo log data is enabled for table XIAOXU.SXIAOXU.
Columns supplementally logged for table XIAOXU.SXIAOXU: AGE, SEX, ID, NAME.
2、构建5条完全一样数据
代码语言:javascript复制insert into xiaoxu.sxiaoxu values(1,'xiaoxu',18,'m');
insert into xiaoxu.sxiaoxu values(1,'xiaoxu',18,'m');
insert into xiaoxu.sxiaoxu values(1,'xiaoxu',18,'m');
insert into xiaoxu.sxiaoxu values(1,'xiaoxu',18,'m');
insert into xiaoxu.sxiaoxu values(1,'xiaoxu',18,'m');
3、验证同步情况
3.1针对插入完全没有问题
--extract抽取情况
代码语言:javascript复制GGSCI 1> stats txiaoxu
Sending STATS request to EXTRACT TXIAOXU ...
Start of Statistics at 2019-04-03 21:17:22.
Output to ./dirdat/xu:
Extracting from XIAOXU.SXIAOXU to XIAOXU.SXIAOXU:
*** Total statistics since 2019-04-03 21:16:00 ***
Total inserts 5.00
Total updates 0.00
Total deletes 0.00
Total discards 0.00
Total operations 5.00
--replicat进程
GGSCI> stats rxiaoxu
Sending STATS request to REPLICAT RXIAOXU ...
Start of Statistics at 2019-04-03 21:18:43.
Replicating from XIAOXU.SXIAOXU to XIAOXU.TXIAOXU:
*** Total statistics since 2019-04-03 21:17:11 ***
Total inserts 5.00
Total updates 0.00
Total deletes 0.00
Total discards 0.00
Total operations 5.00
3.2 一次性更新全部5条数据,目标同样没有问题
代码语言:javascript复制update xiaoxu.sxiaoxu set name='xuxiao';
--检查源和目标数据完全一致的.对于源端5条更新,在目标更新都加上rownum=1来保证每次都更新一条.对logdump中类型是GGSPKUpdate.
通过打印SQL,目标端执行如下更新,set和where都加上所有列且where后面加上rownum=1来保证每次更新都是一条.
代码语言:javascript复制UPDATE "XIAOXU"."TXIAOXU" SET "ID" = '1',"NAME" = 'xuxiao',"AGE" = '18',"SEX" = 'm' WHERE "ID"='1' AND "NAME"='xiaoxu' AND "AGE"='18' AND "SEX"='m' AND ROWNUM = 1
Statement length: 164
(S)top display, (K)eep displaying (default):
3.3 源端随机更新一条,目标只能从第一条处理,只是顺序乱,但是数据是一致的.
--表数据以及rowid情况
比如把sxiaoxu中最后一条记录的name更新xiaoxu,目标其实是更新第一条.
代码语言:javascript复制update xiaoxu.sxiaoxu set name='xiaoxu' where rowid='AADwvrAAFAACEqkAAE';
commit;
目标表txiaoxu,是更新第一条数据
3.4 如果源表表结构发生,如是大字段会造成数据不一致问题.如是非大字段,必须重新删除附加日志,再次增加,否则会造成无法捕获增加列更新情况.
备注:针对大字段是无法加入到附加日志中.所以where中无大字段列,导致缺少一个列来判断重复值,导致更新异常.
代码语言:javascript复制alter table xiaoxu.sxiaoxu add (addresss clob);
alter table xiaoxu.txiaoxu add (addresss clob);
--重启抽取进程以及应用进程
--验证附加日志,发现附加日志并自动更新,需要重新删除后再增加,否则会造成extract无法捕获到新增列更新的情况.
代码语言:javascript复制GGSCI () > info trandata xiaoxu.sxiaoxu
Logging of supplemental redo log data is enabled for table XIAOXU.SXIAOXU.
Columns supplementally logged for table XIAOXU.SXIAOXU: AGE, SEX, ID, NAME.
【验证插入数据】
--验证源端插入数据,插入数据在最后一行
--验证目标插入数据,发现插入数据变成第一行
【验证更新数据】
--源端更新数据最后一条数据
代码语言:javascript复制update xiaoxu.sxiaoxu set addresss='newshanghai' where rowid='AADwvrAAFAACEqkAAF';
commit;
--目标检查数据--更新无异常
--源端更新数据第2条数据,name='xiaoxu'时,目标还是更新第一条数据.
代码语言:javascript复制update xiaoxu.sxiaoxu set addresss='oldshanghai1' where rowid='AADwvrAAFAACEqkAAE';
commit;
--目标检查数据--目标还是更新第一条数据,因为addresss附加日志没有捕获到.
3.5 如果源表表结构发生,比如增加字段,对于附加日志是否需要删除重新增加由以下条件决定:
a:如果附加日志本身就是all columns的情况,那么增加或者删除列的情况,重启抽取进程即可,all columns分为2种情况,1种是超过32列时,增加附加自动all columns,2种sql命令手动指定all columns来实现(列小于32也没有问题,取消超过32列限制),11版本的ogg中ggsci窗口还没有all columns,12c的ggsci中支持. 11g只能通过sql命令alter table xiaoxu.sxiaoxu add supplemental log data(all) columns;
b:如果附加日志本身是conditional(有条件),增加或者删除列后,需要删除原来附加日志信息,再次增加新附加日志信息,然后重启抽取进程--主要是为了刷新数据字典缓存信息,如果没有更新附加日志或者没有重启抽取日志,原来有值,更新其他字段时,会造成这个字段在目标被更新成NULL,这个是不规范造成,从而造成后续数据不一致,出现常见错误1403错误.
c:如果增加、删除、修改的是大字段类型的列,例如clob,这个列无法增加附加日志中,那么则忽略附加删除删除与增加问题.但是抽取进程还是要重启来刷新数据字典缓存信息.
代码语言:javascript复制alter table xiaoxu.sxiaoxu add ( addresss varchar2(30));
alter table xiaoxu.txiaoxu add ( addresss varchar2(30));
--插入数据
如果抽取进程不重启,则捕获到空值.
代码语言:javascript复制INSERT INTO "XIAOXU"."TXIAOXU" ("ID","NAME","AGE","SEX","ADDRESSS") VALUES ('1','xiaojing1','18','m','')
Statement length: 106
如果抽取进程重启,则捕获到到正常值
代码语言:javascript复制INSERT INTO "XIAOXU"."TXIAOXU" ("ID","NAME","AGE","SEX","ADDRESSS") VALUES ('1','xiaojing2','18','m','shanghai')
Statement length: 114
--验证更新(重启抽取进程,trandata是没有这个字段)
代码语言:javascript复制update xiaoxu.sxiaoxu set name='newxiaojing3' where rowid='AADwvrAAFAACEqkAAJ';
commit;
select rowid,a.name,to_char(addresss) from xiaoxu.sxiaoxu a;
AADwvrAAFAACEqkAAJ newxiaojing3 shanghai
目标更新--发现源端地址变成空值了,因为附加日志问题导致的.如果更新地址这一列则没有问题,如果不更新地址这一列,直接变成NULL,注意这个坑。
代码语言:javascript复制UPDATE "XIAOXU"."TXIAOXU" SET "ID" = '1',"NAME" = 'newxiaojing3',"AGE" = '18',"SEX" = 'm',"ADDRESSS" = NULL WHERE "ID"='1' AND "NAME"='xiaojing3' AND "AGE"='18' AND "SEX"='m' AND "ADDRESSS" IS NULL AND ROWNUM = 1
Statement length: 214
3.6 如果使用keycols,keycols的值存在相同,但每一条记录所有字段组合是唯一的情况,如果存在2条记录,更新最后一条,如果源与目标记录顺序一致,那么更新会造成错误错误,如果记录顺序刚好相反,那么更新则没有问题.那么这个时候使用keycols是不合适的,没有达到标识唯一的记录,虽然速度会快些,还不如去掉keycols来保证数据完整性.
三【针对无主键无唯一索引的表,goldengate优化方案】
1、修改表结构,增加一个虚拟列使用sys_guid()函数来产生唯一值。这个需要考虑到表结构更改以及增加字段时间,对于新表增加影响会小,但是已存在大表,需要停机窗口来增加字段.
2、使用keycols来指定标识唯一值列(可以是多列),如果选出列无法满足,会出现如下情况
如果存在2条记录,更新最后一条,如果源与目标记录顺序一致,那么更新会造成错误错误,如果记录顺序刚好相反,那么更新则没有问题.那么这个时候使用keycols是不合适的,没有达到标识唯一的记录,虽然速度会快些,还不如去掉keycols来保证数据完整性.
3、无主键无唯一表,表结构发生变化,必须删除附加日志,再次增加附加(针对add trandata方式增加,如果是add schematrandata不存在这个问题),否则会出现数据不一致情况,主要schematrandata与trandata实现方式不同,trandata是ddl操作,对于高并发表无法获取锁,schematrandata通过调用接口实现,取消ddl加锁.后续会具体分析下.
4、针对无主键的全列附加日志,对于线上系统,尤其是oracle数据库,如果是大表全列附加日志,需要考虑数据库归档空间增加以及OGG抽取给数据库造成额外IO影响,需要评估下.