在测试阿里云的sqlserver到mysql的dts同步,且是带过滤条件的同步,发现更新有个“能增不能减”的问题。在做带过滤的update,把过滤字段更新成其他值,目标端只能增不能减。—— 即过滤字段update成过滤条件,在目标端会增加改行记录; 过滤字段从符合过滤条件update成不符合过滤条件,在目标端不会自动删除该行记录。
看下面的例子:
dts实例id : dtshrj10404232y2vg
源端:sqlserver: 实例id:rm-wz97281ap59lcz4md
目标端:mysql: 实例id:rm-wz94c40229w5qdze1
过滤条件,table1的myid字段, myid=5
Case a:
(一) 更新前:
源端(sqlserver):
Case b:
(一) 更新前: 源端(sqlserver):
原因分析:
我们在测试sqlserver到mysql的,带过滤条件的dts同步,update过滤字段,同步能增加不能减少,如,我的过滤条件为myid=5
a)比如 update table set myid=5 where myid=4 ,即将不符合过滤条件的字段,update成符合过滤条件的字段,这个是能同步过去到mysql —— 能增加
b) 比如 update table set myid=1 where myid=5,即将符合过滤条件的字段(已经存在于目标端),update成不符合过滤条件的字段,在mysql端不会修改或删除这条记录。 —— 不能减少。
Case a:update 前镜像(指更新前的记录)不命中filter的条件(即当时myid=4,而非myid=5),后镜像(指更新后的记录)命中filter的条件(即myid=5)。DTS转换成insert写入。这个符合预期。
Case b:update 前镜像命中filter条件(即myid=5),后镜像不命中(即myid=1)。实际情况DTS忽略这条update语句,这是因为SQL Server-Reader只能解析出聚集索引列值,其他字段前镜像填充none,后镜像通过源库查询获取完整镜像。如下:
那么case2就很好解释了,上面这条record前后镜像都没有命中filter条件,所以这条update过滤掉了。(更新前,虽然myid=5,但是reader解析只能解析出聚集索引的值,其他值填充NONE,所以myid=none了,没命中。更新后,myid=1,也没命中)
Case c(case b改进后):
源端(sqlserver):
1 2 3 4 5 |
alter table table1 drop constraint PK__Table1__3214EC277618E03B; alter table table1 alter column myid int not null; alter table table1 add constraint pk_table1_id_myid primary key(id,myid); |
目标端mysql:
(先清理数据,保证源端和目标端数据量一致。目标端清除掉id=12的记录)
解决方案:
1、创建中转库(sqlserver: rm-wz9f55829gc81yi89),将需要过滤的表,先创建表结构,注意将需要过滤字段做成和pk复合的索引。
2、dts非过滤同步(dts: dtsgc310pxq23si17b),但只同步需要过滤表,不需要全库同步。从Sqlserver-source(rm-wz97281ap59lcz4md)到sqlserver-interim(sqlserver:rm-wz9f55829gc81yi89)。
3、dts过滤同步(dts:dtsmtu102gp23c40q0),增加过滤条件,从sqlserver-interim(sqlserver:rm-wz9f55829gc81yi89)到mysql(mysql:rm-wz94c40229w5qdze1)。注意在mysql端也先建好表结构,不需要把过滤条件放入到主键。
题外话:
sqlserver为源端,过滤dts有这个问题,是sqlserver的日志格式导致的了。
类似的mysql和pg没有这个问题,但pg需要在搭建dts任务的过程中,执行一个ALTER TABLE