ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

MySQL到达梦数据库快速迁移实操:从环境准备到数据校验的完整指南

MySQL到达梦数据库快速迁移实操:从环境准备到数据校验的完整指南

很多做数据库迁移的朋友,看到“MySQL到达梦”几个字,第一反应就是“又是个大工程”。实际上这两年在配合业务做数据库迁移时,我接触最多的反而是几十张表、单表几十万行的中小型业务库。这类体量只要网络正常、对象结构别太离谱,真正从连接源端到迁移执行完,三分钟内跑完完全有可能。今天就把我反复跑过的一套流程拆开来讲:三分钟是哪一部分的三分钟、动手前要做什么、两条实际可用的迁移路径,以及迁移完之后最容易忽略的校验和排错环节。

1. 先弄明白:“三分钟迁移”到底是哪三分钟

1.1 我实际接触的MySQL到达梦迁移场景

这两年配合业务方做过的MySQL到到达梦数据库切换,大概能分成三类。一类是业务系统重新选型,不再用原本的MySQL实例;一类是公司里MySQL实例太多,想逐步统一到一个数据库平台上管理;还有一类是新项目直接落在达梦上,需要把老MySQL库里积累的历史数据搬过来做初始化。这些场景有个共同特点:都不是那种几百亿行、秒级写入的核心账务库,而是几十张表、结构相对规范、单表数据量在几十万到几百万行的普通业务库。

这种体量下,只要迁移链路本身是通的,数据搬运这个执行阶段确实能做到分钟级。真正耗时的是搬之前的对象盘点,以及搬完之后的数据校验和应用SQL兼容性改造。如果上来就对着一个几千万行的大表谈“三分钟”,那肯定不现实,这个问题我会在第5章专门讲清楚。

1.2 三分钟的真实边界

我在说“三分钟快速迁移”的时候,心里其实是有一个默认前提的:源端MySQL连接信息明确,目标端达梦实例已经初始化好,账号权限已放通,待迁对象的基本情况已经盘点过。在这个前提下,用达梦自带的DTS迁移工具,从连接源端、勾选对象、开始执行到跑完,一个小规模库是可以控制在几分钟内的。

如果把建库、建用户、调服务参数、应用改造都算进去,那按小时甚至按天算都正常。所以建议你先对号入座:如果你手头的库是几十张表、表中没有大字段、没有几十个存储过程,那值得按“三分钟迁移”的思路来准备;如果你的库单表超千万行、分区表一大堆、应用里全是MySQL专有函数,那就别指望净迁移三分钟了,老实用分批迁移加增量同步的方案。

1.3 迁移方案选择的判断标准

我每次迁移前都会先回答三个问题:能不能停库,目标端是不是只有自己用,应用能不能接受短期数据不一致。

能停库、目标端是测试环境,直接走全量迁移最省事,DTS一跑,校验通过,收工。不能停库、源端还在持续写入,那就别想着一次把所有数据搬完。先做结构迁移,再在低峰期做一次静态数据导出,如果讲究一点,还要配增量同步来追平停机窗口期间的新数据。这个问题想不清楚,后面所有步骤都可能白做。

2. 动手前把这三件事做了,迁移才会快

2.1 连接信息与目标模式登记表

我不太相信记忆力,每次迁移前都会做一张信息登记表,先确认源端和目标端的基本信息,再动手去连。

项目源端MySQL目标端达梦
实例地址10.10.x.x:330610.10.y.y:5236
数据库/模式名companydbCOMPANY
登录账号readonly_userSYSDBA
字符集utf8mb4UTF-8
待迁对象全部表和视图提前建好的目标模式

这张表看着简单,但很多迁移翻车都发生在目标端模式名写错、源端账号权限不够这些低级问题上。建议每填一行,就在对应环境里执行一条实际语句验证一次,不要凭记忆猜。比如源端用mysql -u账号 -p -e "show databases"确认连通性和可见库,目标端用达梦的disql执行一句select 1 from dual;确认能连上。

2.2 对象清单盘点:能自动迁的和必须手工改的

在源端我会跑两条SQL,把库里的表、视图、存储过程、函数、触发器全部列出来。表的部分重点看有没有自增列、JSON字段、二进制大字段、分区表;函数和存储过程部分重点看有没有用到MySQL独有的语法。

看完之后给对象分三档:第一档是标准表,DTS基本能自动迁;第二档是结构特殊一点的,比如自增列、特殊默认值,迁移后需要人工核对建表语句;第三档是存储过程、函数、触发器里用了group_concat、date_format、ifnull这类MySQL习惯写法的,基本都要手工改造。把这些预先标出来,绝对比等导入报错再翻日志快得多。

2.3 权限准备与连通性验证

源端MySQL账号至少要能全表select,如果库里有视图和触发器,还得有show view和trigger权限。目标端达梦账号需要有建表、建索引、建视图的权限。如果目标模式还没建,先用管理员账号建好模式、赋好权限,不要等到DTS执行到一半才想起来权限不足。

这部分有个很容易忽略的点:MySQL和达梦的默认端口不一样,如果目标实例在另一台机器上,务必提前确认防火墙放行了5236端口。网络不通的时候,DTS界面会一直转圈,非常浪费时间。连通性测试宁可多做一次,也不要等到迁移任务超时再回头查。

3. 三分钟迁移实操:两条路径任选

3.1 路径A:达梦DTS迁移工具的可视化迁移

DTS是达梦安装包自带的图形化迁移工具。它在“MySQL到达梦”这个方向做了不少默认处理,比如把MySQL的反引号标识符转换掉,把AUTO_INCREMENT映射成达梦的IDENTITY自增列,把datetime映射成时间戳类型。这也是为什么我遇到中小型库,首选一定是DTS。

实际操作逻辑大致是这样的:

  1. 启动DTS,新建工程,新建一个迁移任务;
  2. 源数据库类型选MySQL,填源端连接信息;
  3. 目标数据库类型选DM,填目标实例连接信息;
  4. 勾选需要迁移的对象,第一次建议只勾表和视图,不勾存储过程;
  5. 在类型映射界面逐项检查一遍,看到带感叹号的对象就点开手动确认;
  6. 设置批量提交行数和出错处理方式,然后点执行。

执行过程中DTS会逐表显示迁移状态。我印象里,单表几十万行的普通表,一张大概跑几秒到十几秒;几十张表不加存储过程的话,整个任务跑完也就一两分钟。跑完后它会生成迁移报告,哪张表失败、失败在哪条SQL,都能看到。

这里有个关键心法:别把希望全压在DTS上。迁移完不等于结构完全一致。我每次跑完都会在目标端挑几张典型表看建表语句,重点确认自增列、默认值、字段注释。如果发现工具把某个字段类型映射得和自己预期不一致,下次迁移前先手动调整目标表,用不了两分钟,却能省掉应用层的一堆报错排查时间。

3.2 路径B:mysqldump导出加DISQL导入的命令行方案

在没有图形界面的服务器上,或者想把迁库过程写进自动化脚本里,走命令行更顺。我的做法分三步。

第一步,在MySQL端导出数据:

mysqldump -h10.10.x.x -P3306 -u账号 -p \ --single-transaction --skip-lock-tables \ --default-character-set=utf8mb4 \ --no-tablespaces companydb > companydb.sql

--single-transaction能保证导出期间InnoDB表读到的是一致性快照,不会锁住业务表;--no-tablespaces是去掉表空间相关语句,避免达梦不识别。

第二步,对SQL文件做轻量清洗。最常见的是建表语句末尾的ENGINE=InnoDB DEFAULT CHARSET=utf8mb4子句,以及AUTO_INCREMENT。我习惯写个小脚本处理正则,不建议手工改大文件。文件里如果出现大量反引号,尽量去掉,达梦会把双引号当标识符引用符,反引号基本不受支持。

第三步,用DISQL导入:

/opt/dm/bin/disql SYSDBA/密码@localhost:5236 SQL> start /backup/companydb.sql

这个方案比DTS多几步,但胜在可控。导出文件有问题,可以只改文件重新执行,不用每次都在图形界面里重新勾一遍对象。它也比较适合“把数据导到文件,再交给别人到目标环境导入”的工作模式。缺点是SQL语法兼容性需要自己打理,如果源库建表语句里带了太多MySQL习惯写法,清洗脚本要反复迭代几次才能稳定。

3.3 让执行阶段快起来的三个控制点

无论DTS还是命令行导入,真正影响迁移耗时的就是三个变量:网络往返、单条SQL执行开销、事务提交频率。

第一,批量提交。DTS里的“批记录数”我一般设5000到10000。设太小,每条记录都走一次提交,慢;设太大,目标端日志压力上来,反而卡。命令行导入SQL文件的话,可以在清洗阶段就把多行insert合并成一条,减少解析和执行次数。

第二,临时关闭外键校验。有父子表关系的库,按默认顺序导数据时外键约束很容易拖后腿,甚至报错。DTS里可以勾选“导入数据时忽略外键”,也可以先只迁移表结构,等数据导完再补外键约束。顺序上,我永远是“先结构、后数据、再约束”。

第三,确认目标端日志参数不是最低配置。导入期间如果目标库的日志频繁切换,整个吞吐会明显下降。这属于达梦实例初始化层面的设置,建议迁移前就确认好,而不是数据导到一半再去调整。

4. 数据搬完只是上半场:校验与收尾

4.1 三层数据校验:行数、总和、抽样

数据迁移完的第一件事,不是关窗口,而是做数据校验。我习惯分三层来查。

第一层,逐表对比行数。在两个库分别执行:

SELECT COUNT(*) FROM 表名;

表多了以后用脚本循环执行,把结果写入文件后对比。手工一张张查不现实,也容易漏。

第二层,数值字段总和对比。针对订单金额、库存数量这类关键数值列,在两个库各跑一遍同样的求和SQL,结果一致,基本能排除类型转换把精度吃掉的情况。

第三层,随机抽样全字段对比。我会在主键范围的首、中、尾各取一条,再随机取十几条,逐字段核对字符串、时间、小数位。这个环节最好写成SQL,用两个库的同名表做连接,把不一致的记录筛出来,而不是肉眼一行行看。

4.2 对象级回归:索引、约束、视图、存储过程

数据没丢,不等于对象没问题。第2章盘点过的对象清单,这时候要拿出来逐项打钩。

索引的数量和定义直接影响后续查询性能。外键约束漏掉一个,应用层就会出现“数据明明在,业务却跑不动”的情况。视图和存储过程更麻烦,DTS通常只能迁一部分,迁完也必须逐条手工验证。我的习惯是:把存储过程在目标端重新编译一遍,看有没有语法报错;再挑两三个最关键的存储过程实际调用一次,看返回结果是否和源端一致。对象级这块是最费时间的,但也是判断迁移是否真正完成的核心标准。

4.3 应用连接串切换前的体检

数据面和对象面都过了,还要检查应用连接串。MySQL的JDBC URL和达梦的JDBC URL差异很大,驱动更是两回事。如果业务系统是Java,要把驱动替换成达梦自带的JDBC驱动,URL改成类似jdbc:dm://10.10.y.y:5236的格式,同时去掉characterEncoding、useSSL这些MySQL专用参数。

连接串改完,别急着全量切流。先用一个最小工具直接连目标库,验证基本读写,再安排一个应用实例切过去跑几分钟,重点看日志里有没有SQL报错。统计上,结构迁移和数据迁移都通过,不代表应用里那几百条SQL全兼容。应用真的跑通了,迁移才算真正告一段落。

5. 实测踩过的坑:五类问题与完整排查链路

5.1 自增列和默认值:迁移后插入新数据报主键冲突

MySQL习惯写id int auto_increment primary key,到达梦后最稳的写法是id int identity(1,1) primary key。DTS一般能处理,但命令行导入时如果建表语句是自己写的,很容易漏掉identity。漏掉的后果是:历史数据导进去了,应用插入新记录时主键冲突。排查最快的方法就是看目标端建表语句,确认identity在不在,不在就重建表或手工补上。

5.2 表名大小写与标识符引号

MySQL在Linux下默认表名小写,达梦默认会把未加引号的标识符统一转成大写。应用如果写的是小写表名,一般能匹配;但如果用驼峰命名,或者建表时用了双引号,大小写就会变得极其敏感。这个问题的根治思路是在达梦实例初始化阶段就想清楚大小写策略,建库前定规则,不要等数据迁完才发现应用SQL对不上。

5.3 字符集乱码:文件的编码和库的编码必须一致

导出文件是UTF-8,目标库字符集不是UTF-8,中文基本就会乱。另一种常见情况是源端MySQL字段实际用了latin1,导出时又强制指定utf8mb4,数据经过双重转换后各种怪字符。

排查乱码时我会卡三个阶段:MySQL连接字符集、导出文件的实际字节、达梦客户端导入时的字符集设置。用file命令看一眼导出文件编码,再用十六进制工具看中文字节,基本能定位是哪一环出了问题。

5.4 大事务卡住与锁等待

有一次导一张接近百万行的表,跑到三分之一就停住不动。我第一反应是看DTS日志,报的是锁等待超时。再看目标端,原来有个测试连接一直在占着表锁。处理方法是:把占用锁的会话停掉,DTS任务重新跑,并把批提交行数从默认值调到5000,问题解决。

这类问题如果发生在生产环境,千万别直接重启数据库。先查活动会话和事务,确认是谁持有锁,再做针对性处理。迁移过程中的卡顿,十有八九是锁和事务提交频率的问题,不是数据量的问题。

5.5 函数差异:不一定会报错,但结果可能是错的

比语法报错更棘手的是“不报错但结果不对”。MySQL的concat和达梦都能用,但null处理逻辑不同;MySQL的ifnull到达梦要改写成nvl或coalesce;日期格式化函数两边写法差异更大。我建议应用迁过来后,不要只看接口通不通,还要找几条曾经触发过特殊条件的历史数据实际跑一遍,确认函数返回值和原来一致。这种隐性差异最需要时间沉淀,也是迁移项目里最容易被低估的部分。

5.6 把一次卡顿的完整排查链路走一遍

把上面的经验串成一条排查链路,大概是这样的:

看到迁移工具日志定位到具体表 → 在目标端查当前活动事务和锁等待 → 确认锁源是应用连接还是迁移进程 → 如果是迁移进程,调小批提交、临时关闭外键校验再重试 → 如果还不行,把该表数据单独导出导入,排除某一行特殊数据导致整个事务回滚的可能。

这套顺序我用了很多次。每次都能把问题范围缩小到“结构问题”还是“数据问题”还是“并发问题”这个层级,然后快速处理。

6. 快速迁移的边界和我的个人习惯

6.1 哪些场景不该追求三分钟

有几种情况,我会明确告诉业务方别指望净迁移三分钟。单表超过千万行且带大字段,源端MySQL还在高并发写入,存储过程一大堆且大量依赖MySQL特有的动态SQL,目标达梦实例还没做初始化调优,这些场景硬上全量快速迁移,风险远大于收益。

更合理的做法是先做结构迁移,再做分批数据迁移,配合增量同步追平数据,最后开一个短切换窗口。这种思路下,整体迁移周期会拉长,但每一步都稳,回滚也容易。数据库迁移最怕的不是慢,是边迁边出乱子。

6.2 迁移完成后的两个小习惯

第一件,把迁移任务本身写成一份可重复执行的checklist,从信息登记、权限检查到行数校验、对象回归全都列进去。下次再遇到类似任务,照着走一遍,效率高很多,也不容易漏项。

第二件,迁完以后别急着走,把目标库当全新环境来跑一遍重点功能,尤其涉及日期、金额、字符串拼接的页面。数据库迁移不是数据过去就结束,应用真正能用起来才算完成。

最后再分享一个实际体会:三分钟快速迁移能不能成立,很大程度上取决于源端表结构规不规范。表结构越规范,前期准备越短,执行越顺;那些默认值混乱、字段类型满天飞的库,前期清理半小时都未必够。所以别盲目相信任何迁移工具,先把对象清单做清楚,再按这套流程走一遍,三分钟做一个干净的MySQL到达梦迁移是完全可以实现的。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进