震惊!MySQL 和 TiDB 居然有这种行为不一样!

DM 作为一款便携的数据迁移工具,在 MySQL 到 TiDB 的全量数据迁移和增量数据同步中起着很大作用。但由于 MySQL 和 TiDB 并不是完全兼容,所以就可能导致同一条语句在 MySQL 和 TiDB 的执行表现并不一样。

一、万事皆有源

当前架构:上游为 MySQL(一主多从),通过 DM 将部分表同步到下游 TiDB 。用户会提交工单到上游的 MySQL,当然 TiDB 目前并不兼容 MySQL 支持的所有 DDL 语句,这就有可能导致我们的 DM 同步中断。

震惊!MySQL 和 TiDB 居然有这种行为不一样!-1

现象描述,用户提交DDL工单变更字段长度:

alter table table_xxx modify column_xxx varchar(5000) default ” not null comment ‘xxx’。

MySQL 执行正常,但是该语句在 TiDB 执行报错:

“RawCause”: “Error 1265: Data truncated for column ‘xxx’ at row 1″。

根据报错,最先想到的就是查阅文档寻找解决办法,在官方文档搜到如下结果:

震惊!MySQL 和 TiDB 居然有这种行为不一样!-2

但并不是这个原因造成的,咱们继续往下看。

二、分析解谜

冷静下来去看,其实除了将字段长度增加了,还有将 DEFAULT NULL 改成了 DEFAULT ” NOT NULL ,真让人百思不得其解。

下面我们拿一张表分别在 MySQL 和 TiDB 中去做个测试:

CREATE TABLE `test_table` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `c1` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
);

MySQL

震惊!MySQL 和 TiDB 居然有这种行为不一样!-3

TiDB

震惊!MySQL 和 TiDB 居然有这种行为不一样!-4

插入数据

insert into test_table (c1) values(""); 
insert into test_table (c1) values(NULL); 
insert into test_table (c1) values("test");

MySQL

震惊!MySQL 和 TiDB 居然有这种行为不一样!-5

TiDB

震惊!MySQL 和 TiDB 居然有这种行为不一样!-6

执行 DDL

SQL_MODE MySQL 和 TiDB 保持一致,均为 NO_ENGINE_SUBSTITUTION 模式。

震惊!MySQL 和 TiDB 居然有这种行为不一样!-7

MySQL

震惊!MySQL 和 TiDB 居然有这种行为不一样!-8

TiDB

震惊!MySQL 和 TiDB 居然有这种行为不一样!-9

到这里我们看到,同样的一条 DDL 语句在 MySQL 和 TiDB 中表现是不一样的。MySQL 执行成功了,但是 TiDB 执行失败了。

解决:更新 null 为 ”

解决上述问题其实也比较容易,只需要对我们 C1 列为 null 的记录更新为 ” 即可。

震惊!MySQL 和 TiDB 居然有这种行为不一样!-10

修复 DM 同步

知道了上面的原因,我们可以暂时跳过该 DDL 语句恢复 DM 同步,让业务正常运行。

  1. 找到 DDL 的下一个 position

我们的 task-mode 任务模式为 incremental 增量复制,且 enable-gtid 为 false,所以保险的方法我们首先要去寻找 DDL 的下一个位点。

首先根据查询 DM 任务状态我们看到了该 DDL 开始位点和结束位点:

“Message”: “startLocation: [position: (mysql-bin-178-3306.006725, 427465068), gtid-set: ], endLocation: [position: (mysql-bin-178-3306.006725, 427465258)

根据信息解析上游 MySQL 对应的 binlog 文件:

mysqlbinlog –no-defaults –base64-output=decode-rows –start-position=427465068 ./mysql-bin-178-3306.006725 > tmp.sql

震惊!MySQL 和 TiDB 居然有这种行为不一样!-11

  1. 清除元信息,指定位点启动任务

将上述我们找到的位点修改到配置文件中:

====省略其他配置=====
mysql-instances:
-
 source-id: "xxx"
 meta:                                  
   binlog-name: mysql-bin-178-3306.006725
   binlog-pos: 427465321
====省略其他配置=====

除此之外我们还需要清除元信息,启动命令加上 –remove-meta,其目的就是让我们跳过出错的 DDL ,快速恢复业务。

tiup dmctl:vx.x.x --master-addr xxx:xxx start-task xxx.yaml --remove-meta
  1. 与业务沟通,将现有 null 值改为 ”

经过上述测试,在 TiDB 中若字段值为 NULL,想将字段 DEFAULT NULL 改成 DEFAULT ” NOT NULL,那就需要我们先停止任务,记录点位,并在下游 TiDB 执行 update tablename set 列名 where 列名 is NULL。

  1. 更改下游 TiDB 表结构

这里我们要修复表结构,执行 DDL 工单的语句即可。

alter table table_xxx modify column_xxx varchar(5000) default '' not null comment 'xxx'。
  1. operate-schema 更改 DM 中的表结构

现在我们上下游的表结构都是最新的,但是 DM 内部维护的表结构还需要我们使用 operate-schema 进行修改设置(DM v6.0 之后可使用 binlog-schema 命令):

tiup dmctl:vx.x.x --master-addr xxx:xxx operate-schema set -s 数据源 任务名 -d 库名 -t 表名 表结构文件
  1. 恢复任务

进行完上述操作后,上游、下游、DM 内部的表结构已经是一致的,我们恢复任务即可。

三、回顾和展望

上述章节主要描述了在使用 DM 过程中发现的问题,以及如何进行解决,当然可能还有更好的解决办法,也欢迎各位大佬指导。

同时我们也知道,TiDB 和 MySQL 在一些 DDL 的执行上还不是完全兼容。但是通过处理该问题并进行梳理形成总结,可以为日后处理其他相似的情况提供良好的思路进行借鉴。

上一篇 万数汇海,同创共赢 | openGauss Developer Day 2024 圆满举办
下一篇 Flathub 累计下载量已超过 20 亿次