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

2024年 6月 24日 86.6k 0

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

相关文章

Oracle如何使用授予和撤销权限的语法和示例
Awesome Project: 探索 MatrixOrigin 云原生分布式数据库
下载丨66页PDF,云和恩墨技术通讯(2024年7月刊)
社区版oceanbase安装
Oracle 导出CSV工具-sqluldr2
ETL数据集成丨快速将MySQL数据迁移至Doris数据库

发布评论