5
3
2
2
专栏/.../

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

 这里介绍不了我  发表于  2024-06-24

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

一、万事皆有源

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

no-alt

现象描述,用户提交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"。

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

no-alt

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

二、分析解谜

冷静下来去看,其实除了将字段长度增加了,还有将 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

no-alt

TiDB

no-alt

插入数据

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

MySQL

no-alt

TiDB

no-alt

执行 DDL

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

no-alt

MySQL

no-alt

TiDB

no-alt

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

解决:更新 null 为 ''

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

no-alt

修复 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

no-alt

  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 的执行上还不是完全兼容。但是通过处理该问题并进行梳理形成总结,可以为日后处理其他相似的情况提供良好的思路进行借鉴。

5
3
2
2

版权声明:本文为 TiDB 社区用户原创文章,遵循 CC BY-NC-SA 4.0 版权协议,转载请附上原文出处链接和本声明。

评论
暂无评论