一、首先简单介绍一下相关的内容
1、TiDB DM简介:
TiDB Data Migration (DM) 是一款便捷的数据迁移工具,支持从与 MySQL 协议兼容的数据库(MySQL、MariaDB、Aurora MySQL)到 TiDB 的全量数据迁移和增量数据同步。使用 DM 工具有利于简化数据迁移过程,降低数据迁移运维成本。简单来说DM是用来从MySQL向TiDB同步数据的工具,包括:全量同步、增量同步等。
2、pt-osc简介:
pt-online-schema-change在mysql中用来在线执行online ddl的。
流程介绍:
①如果修改表有外键,除非使用--alter-foreign-keys-method指定特定的值,否则工具不予执行;
②创建一个和源表表结构一样的临时表(_tablename_new),执行alter修改临时表表结构;
③在原表上创建3个与insert delete update对应的触发器(用于copy数据的过程中,在原表的更新操作更新到新表);
④从原表拷贝数据(以数据块的形式)到临时表,拷贝过程中在原表进行的写操作都会更新到新建的临时表;
⑤修改外键相关的子表,根据修改后的数据,修改外键关联的子表;
⑥rename源数据表为old表,把新表rename为源表名,并将old表删除;
⑦删除触发器。
二、问题描述
1、tidb版本为5.4.0,dm版本为5.4.0,上游数据库是mysql,300多张表,100多GB数据。
2、发现gtid号相差较大,并且卡住:
3、查看dm-worker日志,发现在狂刷日志,基本都是如下内容:
于是去源端mysql对应的binlog查找该gtid(如下图),发现是使用pt-osc增加字段创建的tmp_pt表从原表导数据的事务(大概300多条):
pt增加字段的命令:
pt-online-schema-change --port=3306 -uroot -s123456 --socket=/tmp/mysql_3306.sock D=test,t=tidb1,A=utf8 --alter-foreign-keys-method=auto --recursion-method=none --no-check-replication-filters --execute --new-table-name=%T_tmp_pt --alter='add COLUMN address8 varchar(11) NOT NULL DEFAULT "aaa" COMMENT "测试"'
4、去tidb端查看,发现tidb中创建了该tmp_pt表。
task文件中设置了onlineddl参数:
为什么还会产生这个问题呢?于是去官网查看onlineddl在dm的流程,简单一下。
三、dm onlineddl流程
这里总结的是使用pt工具的流程,当然还有gh-ost的流程,这里不进行描述,可以去官网查看。
1、pt-osc在实现online的过程会产生2种表:
①new:用于应用ddl的临时表,待表与原表同步完成后,通过rename方式进行替换;
②old:对原表进行的rename操作;
③3个trigger:insert trigger、update trigger、delete trigger,用来同步在拷贝数据过程中产生的dml操作到new表(临时表)。
2、dm处理pt-osc设计的操作:
①pt-osc会创建_new临时表,但是dm不会执行该操作(会根据相关信息删除dm_meta.{task_name}\_onlineddl表中的记录,并清理内存相关信息);
②pt-osc会在_new临时表执行ddl操作,但是dm不执行_new的ddl操作(因为它也没创建该临时表),而是把该ddl记录到{task_name}\_onlineddl表中以及内存中;
③pt-osc会创建3个同步数据的trigger,但是dm不执行,tidb也不支持trigger操作;
④pt-osc会从原表中向_new临时表同步数据,但是dm除了原表的操作,剩下的在临时表的操作均不执行;
⑤pt-osc会在数据拷贝完成后,进行rename操作,这样pt-osc就完成了ddl操作,但是在dm会执行如下两个操作:
把rename语句拆分成两个sql:rename test.test4 to test._test4_old;和rename test._test4_new to test.test4;
不执行rename test.test4 to test._test4_old;该操作,当要执行rename test._test4_new to test.test4;该操作的时候(也就是将执行了ddl操作的临时表改名),并不会去执行rename,而是将第二步记录的ddl操作读取出来把相应的表名进行修改(将_new临时表名改为原来的表名),最后进行应用(就是在原表进行ddl操作);
⑥pt-osc最后会删除3个触发器,但是dm不会执行此操作。
四、问题分析
1、分析:
因为ddl是在sql审核平台执行的,后台调用的pt工具,所以手动在测试执行了一次pt-osc,但是没有添加--new-table-name该参数,发现没有问题,可以执行成功。
于是联系sql审核平台开发老师,打印一下后台日志,查看pt工具生成的命令,最后发现统一了生成规则,添加了--new-table-name参数(由_new结尾变成了_tmp_pt),根据上面的dm处理pt-osc的操作,发现并没有提及改变了临时表名是如何处理,于是在测试库添加了--new-table-name参数问题复现了。
在AskTUG上提了问题,根据官方回复,dm包含了两个隐藏参数,默认参数值如下:
shadow-table-rules = ["^_(.+)_(?:new|gho)$"] //生成临时表的匹配规则
trash-table-rules = ["^_(.+)_(?:ghc|del|old)$"] //老表命名的匹配规则
这两个参数都是正则表达式,根据实际情况是生成_tmp_pt后缀的临时表,修改该参数值:
shadow-table-rules = ["^(.+)_(?:tmp_pt)$","^_(.+)_(?:new|gho)$"] //列表的形式可以直接追加,这样的话可以兼容老的
trash-table-rules该参数没有变,因为虽然添加了 --new-table-name该参数,但是不影响命名的操作,也就是说该参数只改变了生成临时表的名字,我们只需要让dm能够匹配上临时表,这样它才能过滤这些ddl的操作。
2、写入参数文件:
将参数写入task yaml文件中:
假如已经导致了上面的问题了,可能需要重新初始化同步了。如果没有出现问题的时候,参数重启task生效。