Otter 同步出现时间列上值不一致问题

                                         Otter 同步出现时间列上值不一致问题

 

 

背景:

由于历史原因我们DB中引入了多源复制技术,在之后的使用过程中发现了一些弊端,于是准备去掉这套架构,使用otter来代替多源复制。在otter同步跑了很长一段时间之后,我们准备上线这套集群,却发现了一些不一致的情况。

 

问题:

之前otter使用的不是特别多,对其稳定性也不是很有信心,为了检验源库数据和目的数据的一致性,避免切换后出现数据不一致的问题,我们写脚本抽样比对了部分数据,结果发现两边的确存在数据不一致的情况,以下是其中一个案例。

源数据

< 2357 999999999 0 刘 2030420 罗 2409 红娘二部(易) 270 重庆解放碑店 2012218 陈 2020-05-18 02:27:04 2020-05-19 12:20:18 NULL 0 0 0 0 2020-05-19 12:20:18 2020-05-19 12:20:18

目的数据

> 2357 999999999 0 刘 2030420 罗 2409 红娘二部(易) 270 重庆解放碑店 2012218 陈 2020-05-18 02:27:04 2020-05-19 12:20:18 NULL 0 0 0 0 2020-05-19 12:20:18 2020-05-27 10:53:37

表结构

CREATE TABLE `ArchiveMeetDetail` (

  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',

  `memberId` int(11) NOT NULL ,

  `sex` tinyint(3) NOT NULL ,

  `trueName` varchar(10) NOT NULL ,

  `belongWorkerId` int(11) NOT NULL ,

  `belongWorkerName` varchar(20) NOT NULL ,

  `belongGroupId` int(11) NOT NULL ,

  `belongGroupName` varchar(20) NOT NULL ,

  `belongDeptId` int(11) NOT NULL ,

  `belongDeptName` varchar(20) NOT NULL ,

  `workerId` int(11) NOT NULL ,

  `workerName` varchar(255) NOT NULL ,

  `archiveTime` datetime NOT NULL ,

  `archivePassTime` datetime NOT NULL ,

  `firstMeetTime` datetime DEFAULT NULL ,

  `meetThisMonth` int(11) NOT NULL DEFAULT '0' ,

  `miniMeetThisMonth` int(11) NOT NULL DEFAULT '0' ,

  `meetTotal` int(11) NOT NULL DEFAULT '0' ,

  `miniMeetTotal` int(11) NOT NULL DEFAULT '0' ,

  `createTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ,

  `updateTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ,

  PRIMARY KEY (`id`),

  KEY `idx_member_id` (`memberId`) USING BTREE,

  KEY `idx_group_id` (`belongGroupId`) USING BTREE,

  KEY `idx_dept_id` (`belongDeptId`) USING BTREE,

  KEY `idx_archive_pass_time` (`archivePassTime`) USING BTREE

) ENGINE=InnoDB AUTO_INCREMENT=3543 DEFAULT CHARSET=utf8

我们发现:

1.都是这种updatetime时间列上的数据不一致。

2.updatetime列上有ON UPDATE CURRENT_TIMESTAMP 子句,也就是每次更新的时候这列的值会自动更新为当前时间。

 

排查:

首先我们在源库解析binlog,提取对这个表修改的sql,主要是两个sql,一个update语句和一个insert语句。

我们测试了update 语句在修改其他列或updatetime 列的情况,也测试了insert在指定updatetime值和不指定updatetime 值的情况(第一次可能测试的不是很仔细),结果都是源和目的的数据是完全一致的。

之后,根据目的数据的修改时间(2020-05-27 10:53:37),我们排查目的库的binlog,找到修改这条记录所对应的sql。

UPDATE `zhenai_crm_matchmaker`.`ArchiveMeetDetail` SET `firstMeetTime`=NULL, `updateTime`='2020-05-27 10:53:37', `miniMeetTotal`=0, `archiveTime`='2020-05-18 02:27:04', `belongDeptName`='重庆解放碑店', `workerId`=2012218, `createTime`='2020-05-19 12:20:18', `belongGroupId`=2409, `memberId`=999999999, `belongWorkerId`=2030420, `sex`=0, `belongGroupName`='红娘二部(易)', `trueName`='', `belongWorkerName`='', `meetTotal`=0, `workerName`='', `miniMeetThisMonth`=0, `belongDeptId`=270, `meetThisMonth`=0, `id`=2357, `archivePassTime`='2020-05-19 12:20:18' WHERE `firstMeetTime` IS NULL AND `updateTime`='2020-05-19 12:20:18' AND `miniMeetTotal`=0 AND `archiveTime`='2020-05-18 02:27:04' AND `belongDeptName`='重庆解放碑店' AND `workerId`=2030420 AND `createTime`='2020-05-19 12:20:18' AND `belongGroupId`=2409 AND `memberId`=999999999 AND `belongWorkerId`=2030420 AND `sex`=0 AND `belongGroupName`='红娘二部(易)' AND `trueName`='' AND `belongWorkerName`='' AND `meetTotal`=0 AND `workerName`='' AND `miniMeetThisMonth`=0 AND `belongDeptId`=270 AND `meetThisMonth`=0 AND `id`=2357 AND `archivePassTime`='2020-05-19 12:20:18' LIMIT 1; #start 142683935 end 142684421 time 2020-05-27 10:53:37

如果目的库这个时间有修改,那么源库这个时间应该也是有同样的sql在执行,顺着这条思路,查查了源库binlog,解析出了对应的sql。

UPDATE `zhenai_crm_matchmaker`.`ArchiveMeetDetail` SET `firstMeetTime`=NULL, `updateTime`='2020-05-19 12:20:18', `miniMeetTotal`=0, `archiveTime`='2020-05-18 02:27:04', `belongDeptName`='重庆解放碑店', `workerId`=2012218, `createTime`='2020-05-19 12:20:18', `belongGroupId`=2409, `memberId`=999999999, `belongWorkerId`=2030420, `sex`=0, `belongGroupName`='红娘二部(易)', `trueName`='', `belongWorkerName`='', `meetTotal`=0, `workerName`='', `miniMeetThisMonth`=0, `belongDeptId`=270, `meetThisMonth`=0, `id`=2357, `archivePassTime`='2020-05-19 12:20:18' WHERE `firstMeetTime` IS NULL AND `updateTime`='2020-05-19 12:20:18' AND `miniMeetTotal`=0 AND `archiveTime`='2020-05-18 02:27:04' AND `belongDeptName`='重庆解放碑店' AND `workerId`=2030420 AND `createTime`='2020-05-19 12:20:18' AND `belongGroupId`=2409 AND `memberId`=999999999 AND `belongWorkerId`=2030420 AND `sex`=0 AND `belongGroupName`='红娘二部(易)' AND `trueName`='' AND `belongWorkerName`='' AND `meetTotal`=0 AND `workerName`='' AND `miniMeetThisMonth`=0 AND `belongDeptId`=270 AND `meetThisMonth`=0 AND `id`=2357 AND `archivePassTime`='2020-05-19 12:20:18' LIMIT 1; #start 126420599 end 126421098 time 2020-05-27 10:53:37

单看每一边的binlog及表的数据,都没有什么问题,binlog和数据都是对应的。但问题是同步过去为什么数据不一样,当然是因为执行的sql不一样。可是执行的sql为什么不一样呢?难道同步过去binlog不一样。但是这不可能的,主库和从库的binlog应该是一样的,

那这里目的库的updatetime值为什么跟主库不一样呢?仔细看,就可以发现两点:

1.源库在修改这条数据的时候也修改了updatetime 的值,但不是修改成最新的时间,而是跟原来一样的时间。

2.而目的库,updatetime则是较新的一个值,很可能是当时执行时的时间。

基于以上两点我们又做了测试,终于发现了问题。

 

 

验证:

 

测试SQL

结果

1

update set updatetime = '2020-05-19 12:20:18' , workerName = 'test' ...

两边数据不一致

2

update set updatetime = '2020-05-28 10:00:00' , workerName = 'test' ...

两边数据一致

我们发现出现不一致要有两个条件,

第一,updatetime的值要修改成跟原来一样,也就是updatetime的值要保持不变,

第二,除了updatetime列外,还需要变更其他列的数据。

对于源库来说,它在执行update的时候把updatetime列的值修改成跟原来一样的值(如果不这么做,那么updatetime的值就会是执行时的时间)。而binlog传到目的端,在解析binog的时候,(由于一些参数)otter会根据列的变更来同步数据,而updatetime这列修改前的时间和修改后的时间是一样的,otter就没有同步这列,比如第一个sql,otter解析执行的sql可能是这个样子的:update set workerName = 'test' ... 。由于updatetime这列是自动更新的,所以实际是这样的:update set updatetime = '#current_time#' ,workerName = 'test' ... 。这就导致了目的时间是当前时间,和源库的时间不一样。

 

解决:

查询资料及官方文档,我们找到了otter中的一个可能造成这种结果的参数--channel 中的同步模式,有两种同步模式:
行模式 ,兼容otter3的处理方案,改变记录中的任何一个字段,触发整行记录的数据同步,在目标库执行merge sql。

列模式 ,基于log中的具体变更字段,按需同步。

原先我们设置的是列模式,对于一般情况,同步不会有问题,但无法处理上面这种特殊情况下的同步。改为行模式后,经测试,两边的数据一致。

 

posted @ 2021-03-13 22:21  AYard  阅读(437)  评论(0编辑  收藏  举报