DM迁移流程或原理

请问哪里有DM迁移 mysql 的详细过程(原理)?
目前主要有这些疑惑:

  1. dump阶段同,是保证了事务一致性,还是没有?(参考mysqlump 全备的–single-transaction)
  2. sync阶段,是在 load 全量完成之后进行?还是与 load 并行?
  3. sync 怎么保障与 binlog 相同的顺序?是单表保证,还是task级别保证?目前遇到两个问题,一直没弄清楚原因
    a) 主键冲突,已经解析binlog排查,这个表的看你咯是每天 truncte table 生重新 insert,没有其他操作,也没有插入重复主键
    b) 一个表 rename 之后,修改表结构,又rename 回来,再改表结构,在tidb这边出现提示表不存在,从执行的语句看,是在执行最后一次alter,但第二次rename还没有执行,导致表还没有rename回来

保证了一致性,有好几种方式,有一种是加锁后,取位点,开始备份数据。有一种是不加锁,取位点,开始备份,然后回放时可能会重复回放一点,用replace的方式回访就实现了幂等。最终一致。

load完成后开始sync

最后俩问题等其他人解释下吧,我理解就是binlog解析成sql后执行,什么原因导致的错误不太清楚。

1 个赞

可以看一下303课程或者看一下源码

这个是遇到的一个实际问题,大家可以一起看看
这也是为什么我比较纠结原理(看源码要的时间太长了,如果有熟悉的朋友,可以更快的解决问题)

表结构
t1(idn int primary key, c1 int)

myssql – 上游表 (无记录,上下游表中的数据不一致,是由于BUG导致, TRUNCATE TABLE语句不会被同步)
id c1


tidb – 下游表
id c1


1 1
3 3

现在在上游表中做如下 insert
insert into t1(id, c1) values(1, 11);
insert into t1(id, c1) values(2, 22);
此时下游表的数据,同时 dm task 显示 Duplicate entry ‘1’ for
id c1


1 1
2 22
3 3

resume task,可以发现 Duplicate entry 异常消失,下游表的数据如下
(修复的原因是满足 safe mode 触发条件:任务出错停止并恢复后,对有些数据的操作可能会被执行两次时保持启用)
id c1


1 11
2 22
3 3

继续在上游表执行,此过程的表现与第一次相同
insert into t1(id, c1) values(3, 44);
insert into t1(id, c1) values(4, 44);

任务配置
syncers 的 worker-count: 1
safe-mode: false

现在的问题,为什么在 1/3出现Duplicate entry 的情况下,2/4会执行?不应该是顺序执行的么?
如果没有保证顺序,那么如果是做的 insert / delete,那岂不是 delete 先执行了,resume之后会导致表中多一条记录?

我感觉你这个解释的挺清楚的。

你的疑问在于,为什么后面执行

insert into t1(id, c1) values(3, 44);
insert into t1(id, c1) values(4, 44);

这两个insert的时候,又开始报错Duplicate entry?

那是因为safe mode在resume task的一段时间内会生效,过了这段时间就会自动失效。

https://docs.pingcap.com/zh/tidb/stable/dm-safe-mode#自动开启

还是你其实有其他的疑问?

insert into t1(id, c1) values(1, 11);
insert into t1(id, c1) values(2, 22);
— 做这个之前,task 是正常工作的,执行了这个之后,下游tidb写入了 id=2,同时task报错 Duplicate entry 1
这个时候不存在 safe mode 的问题吧,因为 safe mode 配置是关的,task也一直是正常状态

捃后面 id=3/4 的时候,如果是 safe mode 还没有失效的话,那么 3 不应该出错,因为 safe mode = on 还没有失效,但 3 也出错了

我懂你意思了,你的意思是说1已经Duplicate entry报错了,在insert 1后面执行的insert2为啥会在下游出现?是这个意思吧?

问题:为什么没有顺序执行? id=1就错了,id=2就不应该出现在下游表中
id=3/4只是再次复现了这个问题。

如果 insert id=1,2,3,4 这4条语句放一齐执行,就会发现,与只 insert id=1,2的表现一样,id=2出现在tidb中,且task 出错 Duplicate entry 1,然后 resume task,会发现修复了 id=2,这个时候 Duplicate entry=3,但4不会出现在tidb中,需要再次 resume task 才生效

对,就是这个问题

https://docs.pingcap.com/zh/tidb/stable/dm-tune-configuration#worker-count

因为syncer 是多线程并行往下游写入的。

所以inert 1 失败了报错了,但是insert 2写入下游了。

另一个问题,怎么保证一致性?

虽然是并行做的,insert 1失败了,dm_meta库下面对应的表里面记录的binlog位置还是指向insert 1的。
insert 2成功了,但前面的insert 1失败了,所以binlog位置不会指向insert 1后面的位置。

这也就是为什么resume task起来之后需要自动开一小段时间的safe mode。因为像insert 2这类已经被其他线程写入下游的,resume task之后需要重新插入的。

2 个赞

worker-count=1 的情况下也是并行的么?

另外,我总觉得这个并行有点问题呢? 我在上游执行这个:
insert into unified_cache._t1(id,c1) values(1,11); # 这里 Duplicate entry 了,并行的话,这里有没有可能因为晚于下面这个,导致执行通过了,从而出现数据错误?
delete from unified_cache._t1 where id=1; # 这个被执行了

worker-count默认是16,改成1的话,我觉得不会有上面这个问题。insert 2是没有办法在insert 1之前执行成功的。

你这么想,那个sql报错,就停在那里,这样保证错误的dml除非你手工跳过,否则保证一定会在下游执行至少一次。
delete/update 执行多次并不会出现数据不一致。insert的执行多次确实是会报错的,但是被自动的safe mode避开了。到这里已经没有其他类型dml错误的可能了啊。

目前就是在 worker-count=1 的情况下复现的这个问题

insert into unified_cache._t1(id,c1) values(1,11);
delete from unified_cache._t1 where id=1;
— 我的意思是:这两个是并行的话,有可能是先 DELETE 成功,然后 INSERT 就不会再出现 Duplicate entry 了,这样不就导致了数据上下游不一致?

1 个赞

这个我是想不通的。

insert into unified_cache._t1(id,c1) values(1,11);
delete from unified_cache._t1 where id=1;
— 我的意思是:这两个是并行的话,有可能是先 DELETE 成功,然后 INSERT 就不会再出现 Duplicate entry 了,这样不就导致了数据上下游不一致?

你这个情况其实就是假定上游的insert出现duplicate entry 的时候还要写入binlog。这个前提不可能出现。这个insert能写进binlog,可以确定insert之前这个时间表里是没有数据的。

不过这两个不同类的dml乱序执行确实会有问题。这个可能要从代码里面找找答案了。我猜测,并行执行可能只发生在同类语句的时候。

感觉还是得搞懂原理
新遇到的DDL异常,手动在下游执行调整后的DDL,然后 binlog skip,发现后面的一个DDL也报错了,然后检查发现,后面的这个DDL也是在异常的时候就被执行过了

1 个赞

受益匪浅,学习了~!

除了增加表结构更新字段长度这种,其他所有DDL过滤一下。不让他同步,只要在mysql这边操作DDL了。操作完成去tidb再去操作一下不就行了。

这样风险更高,比如RENAME一下,你这样过滤就会导致上下游结构不一致,然后数据可能会放错

今天又遇到一个新问题,上游mysql执行操作
alter table tb
drop column c1,
drop index c1
;
其中 index c1 是 c1 列上的索引,这个在mysql中没有问题,在tidb中是不行的,tidb 必须用两个语句
alter table tb dropp index c1;
alter table tb dropp column c1;
所以 DM 虽然把 mysql 的DDL分解了,但因为顺序不对,所以也导致执行不成功

这个时候我做了一个 binlog replace 操作,将 DDL 的顺序改正确,然后我就发现任务仍然出旬,但是出错的语句中只出现了一条DDL,这个时候手工在tidb执行+binlog skip也玩不转了

接下来,我把任务删除重建,不事 --remove-meta,所以任务又重新从出错的地方开始,又卡在了这个顺序不对的DDL上,这时候我做 binlog skip,神奇的事这次 skip 正常了

然后我又手工建了一个表来验证这个操作,结果更神奇的事发生了,这次我没有做 binlog replace,直接用 binlog skip,结果 skip 之后悲剧了,任务状态显示:
unresolvedGroups.DDLs为空,unsynced: can’t drop column data_code with composite index covered or Primary Key covered now

binlog skip不能消除这个问题,但任务状态是running,并且测试写了一条数据也能同步

找了一下,说是 shard-ddl-lock,查询确实存在这个LOCK,然后我做了 shard-ddl-lock unlock,然后发现问题确实不存在了,但是,这次又丢失 DDL 了,我在测试的时候,上游的 mysql ,最终是把这个测试表 DROP 了的,现在下游的这个表还在,DROP TABLE 的DDL丢失了

受益匪浅,学习了~!