拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MySQL到达梦数据库迁移全流程:结构改写、数据导入与验证

MySQL到达梦数据库迁移全流程:结构改写、数据导入与验证

最近好几个项目的朋友都在跟我吐槽,MySQL迁到达梦数据库的活儿看着简单,真上手就各种报错,网上教程东一榔头西一棒子,很少有一条完整的链路。我自己前段时间刚完整做了一套MySQL到DM8的迁移,从工具选型、数据导出、结构重建,到数据导入、应用适配、线上验证,一路踩坑踩过来,最后整理出了一套能在三分钟内完成迁移的实操流程。这篇文章就把这套流程完整记录下来,不管你是DBA还是后端开发,只要正在做MySQL到达梦的迁移,或者被各种兼容性问题折磨得头大,照着做能少走不少弯路。

1. 迁移方案选型:先想清楚走哪条路

在动手迁移之前,别急着找工具,先想清楚自己手上有哪几条路可以走。目前MySQL到达梦的迁移常见的有三条路线:一是达梦自带的DTS数据迁移工具,图形化界面,从源端到目标端一键操作;二是手工导出SQL脚本再导入,步骤可控、链路透明;三是借助第三方ETL工具,比如DataX、Kettle,适合有数据清洗和转换需求的场景。

我实测下来,三分钟内完成迁移这个目标,用的其实是第一和第二条路线的组合:mysqldump导出MySQL数据,把结构脚本改写之后在达梦端重建表结构,数据量大的表用dmfldr批量加载,数据量小的表直接执行SQL脚本。为什么没有完全依赖DTS?因为DTS虽然界面友好,但在处理复杂对象、特殊类型映射和大量数据时,需要预判很多坑,一旦报错排查起来反而慢。把导出和导入拆开做,每一步都透明可控,这才是“快速完成”的前提。

1.1 达梦自带工具DTS能做什么

DTS全称Data Transformation Service,随DM8安装包一起提供,默认在达梦安装目录的tool子目录下。它的核心工作流是新建迁移作业、配置源端连接、配置目标端连接、选择迁移对象、执行迁移,全程图形化操作,对第一次接触达梦的人来说很友好。

DTS连接MySQL时,需要MySQL的JDBC驱动包,如果本机没有这个jar包,在配置源端连接时要手动指定路径,这一步不熟悉Java环境的人容易卡住。另外,DTS对常规的表、索引、数据行迁移处理得比较成熟,但遇到存储过程、函数、触发器这类复杂对象,它会尝试自动转换,转换后的语法在达梦里很可能跑不通,需要人工二次修复。还有一个经验是,DTS默认配置下,遇到目标表已存在、主键冲突、字段类型无法自动映射等情况,会大量报错,需要你逐条处理,任务量大时很耽误时间。

所以我对DTS的定位是:小表数据搬迁、表结构简单、对象不复杂时,DTS确实省事;但如果是几十张甚至上百张表、带各种复杂对象的库,反而不要过度依赖DTS,拆开做更稳。

1.2 手工导出导入在什么场景下最香

手工方案听起来原始,但在特定场景下是最稳的。先用mysqldump把MySQL侧的结构和数据导出成SQL文件,然后改写脚本里的MySQL语法,最后在达梦侧执行脚本完成数据加载。这样做的好处是链路上每一步都清楚,出了问题能准确定位到是DDL、DML还是类型转换的问题,而不是在一个黑盒工具里瞎猜。

我这次项目里,MySQL端大概有五十多张表,其中十张大表数据量在百万行级别。用DTS迁了一半,中途总是有一些表报主键冲突或类型转换错误,排查起来非常费劲。后来切成“mysqldump导结构 + 拆分数据导出 + 达梦端批量导入”的组合方式,速度反而提上去了。手工方案还有一个附加优势:所有导出的脚本都是文本文件,你可以全局替换、修改、注释掉某一段,随时重跑,这种掌控感在调试阶段特别重要。

1.3 三种路线对比,怎么选

如果你还在犹豫用哪个方案,我整理了一个简单对照:

对比项DTS工具手工导出导入第三方ETL
易用性高,图形界面中,需要命令行基础中,需要额外安装配置
复杂对象支持一般,需人工修复完全可控视工具而定
大数据量性能一般,需调批次参数配合dmfldr性能最好较好
出错排查报错定位不够直观每步都有日志和文件可查依赖工具日志
适合场景表少、结构简单表多、需要掌控迁移过程有数据清洗和转换需求

选型没有绝对答案,核心看你的场景。表少、结构规整,用DTS最省时间;表多、有复杂对象、需要反复调试,手工方案更靠谱;有异构系统数据整合需求,上ETL工具更合理。

2. 三分钟迁移的完整链路拆解

标题说的三分钟,不是指所有场景都是三分钟,而是指一条已经跑通、没有坑的流程,在中小体量数据库下可以做到三分钟内完成核心操作。这个时间包含从mysqldump导出到达梦数据导入完成,不包含前面的方案设计和后面的应用改造,先把这点说明白。如果你带着一个全新的库从零开始,建议先花一小时把流程走一遍,把坑排干净,后面每次重复执行就是三分钟的事。

2.1 导出阶段:mysqldump的参数细节

MySQL侧导出,我用的是mysqldump。命令本身不复杂,关键在于参数选择。

先导出结构文件,命令长这样:

mysqldump -u root -p --single-transaction --set-gtid-purged=OFF \ --databases yourdb --no-data > yourdb_schema.sql

再导出数据文件:

mysqldump -u root -p --single-transaction --set-gtid-purged=OFF \ --databases yourdb --no-create-info > yourdb_data.sql

这里几个参数都有讲究。--single-transaction在InnoDB引擎下能保证导出过程中不锁表,线上导出不会阻塞业务写入,这是必须加的。--set-gtid-purged=OFF也很关键,MySQL开了GTID之后,导出的文件默认会带上SET @@GLOBAL.GTID_PURGED=...这种语句,到达梦里根本无法识别,执行就中断。--no-data和--no-create-info的作用是把结构和数据分成两个文件,方便在达梦侧分别执行,如果一个文件混着DDL和DML,排错时不好定位。

数据量大的表,我建议单独导出,不要把所有数据塞进一个超大SQL文件。比如一张300万行的表,导出的SQL文件可能就有几个GB,不管哪一步出错,重跑一次都是折磨。按表导出可以做到单表维度控制进度,某一张表失败了,单独重跑这一张就行。

2.2 结构改写:从MySQL语法到达梦语法

拿到yourdb_schema.sql之后,直接在达梦工具里执行大概率会报错,原因很简单:MySQL和达梦的DDL语法有差异。我实际操作中主要改这么几类内容。

AUTO_INCREMENT要改成IDENTITY(1,1)。MySQL建表时写id INT AUTO_INCREMENT PRIMARY KEY,达梦等价写法是id INT IDENTITY(1,1) PRIMARY KEY。这里有个细节要提醒:IDENTITY列在达梦中有使用限制,不能随意往里面插入显式值。如果业务数据本身需要在导入时保留原有主键值,建议不要用IDENTITY自增,而是把主键列建成普通INT,由业务程序自行生成主键,这样导入时就不会遇到主键冲突。

行尾定义直接删掉。MySQL建表语句结尾通常带ENGINE=InnoDB DEFAULT CHARSET=utf8mb4,达梦没有这些概念,保留反而报错。

字段注释要改写。MySQL的列注释写在意建表定义里,达梦最稳妥的是建表之后单独执行注释语句:

COMMENT ON TABLE 你的表名 IS '表的说明'; COMMENT ON COLUMN 你的表名.列名 IS '列的说明';

我的习惯是写一个小的文本处理脚本,用正则表达式批量替换常见差异点,比如把AUTO_INCREMENT替换成IDENTITY(1,1),把行尾定义删掉。替换完不要直接执行,先检查一遍有没有漏网的MySQL专属语法,确认无误后再在达梦端跑结构脚本。

2.3 数据导入:小表用脚本大表用dmfldr

结构建好之后,数据导入分两条路。数据量小的表,直接执行数据SQL脚本就行:

disql SYSDBA/******@localhost:5236 -f yourdb_data.sql

注意disql是达梦的命令行客户端工具,执行SQL文件时如果某条SQL报错,默认会继续往下跑,但后面依赖前面数据的SQL可能跟着失败,所以导入后一定要做行数验证。

数据量大的表,我强烈推荐用dmfldr。dmfldr是达梦自带的批量加载程序,用法和Oracle的sqlldr非常像,核心是准备好控制文件(.ctl)和数据文件(CSV)。控制文件示例:

LOAD DATA INFILE '/data/yourtable.csv' INTO TABLE your_table FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' TRAILING NULLCOLS (col1, col2, col3, col4)

CSV数据文件怎么来?可以从MySQL端查询后输出,也可以把mysqldump导出的数据转换格式。我更喜欢用脚本把查询结果直接写成CSV,因为可以控制字段顺序、是否带表头,和控制文件的列映射一一对应。执行导入命令:

dmfldr userid=SYSDBA/******@localhost:5236 control='/data/dmfldr.ctl'

dmfldr执行完会打印统计信息,包括读取行数、处理行数、错误行数。如果错误行数不为0,它会生成.bad后缀的错误数据文件,你可以打开这个文件逐行看是哪些数据出了问题,定位效率非常高。

2.4 快速验证:行数与抽样

导入完成之后,别急着收工,先做一轮快速验证。最简单的是行数对比:对每张表分别在MySQL和达梦执行SELECT COUNT(*),两边数字一致说明行数没问题。但行数一致不等于数据一致,我还会抽几张关键表,随机取几条记录看字段内容是否对应,重点是中文字段、日期字段、金额字段。我一般是写一个对比查询脚本,把两张表的抽样数据拼起来看,人工过一遍心里就有底了。

3. 类型映射与SQL方言差异:最容易翻车的地方

3.1 高频数据类型对照表

迁移过程中最容易翻车的就是数据类型映射。MySQL和达梦的数据类型体系相似但不完全相同,不能生搬硬套。我整理了一个高频对照表,基本覆盖了90%以上的常规业务表字段。

MySQL类型达梦类型说明
TINYINTSMALLINT达梦没有TINYINT,用SMALLINT范围足够
SMALLINTSMALLINT直接对应
INT/INTEGERINT直接对应
BIGINTBIGINT直接对应
DECIMAL(m,n)DECIMAL(m,n)精度保持,注意不能超出达梦上限
FLOAT/DOUBLEFLOAT/DOUBLE直接对应
CHAR(n)CHAR(n)字符长度含义需要验证编码
VARCHAR(n)VARCHAR(n)达梦按字符长度,要确认字符集一致
TEXTTEXT/CLOB建议用CLOB,避免长度不足
TINYTEXT/MEDIUMTEXT/LONGTEXTCLOB统一用CLOB
BLOBBLOB直接对应
DATEDATE直接对应
DATETIMETIMESTAMP达梦TIMESTAMP语义接近DATETIME
TIMESTAMPTIMESTAMP注意默认值表达式差异
BIT(1)BIT长度语义要核对
JSONCLOB达梦没有原生JSON类型,用CLOB存储,应用层解析
ENUM/SETVARCHAR + CHECK约束达梦没有原生ENUM,需要转换

这里说一个我吃过的亏。MySQL的VARCHAR(n)在utf8mb4字符集下是n个字符,达梦的VARCHAR(n)同样按字符计算,看起来一致,但两边数据库实际存储的编码方式有细微差异,尤其是汉字占用空间的计算逻辑。如果达梦库的字符集和MySQL不一致,导入后长度校验可能报错。所以建达梦库的时候,字符集一定要选UTF-8,没有条件也要创造条件重装一次,否则后患无穷。

3.2 应用SQL里的函数与分页差异

数据搬过去了只是第一步,应用侧SQL也得跟着适配。这里列几个最常见的差异点。

分页查询是最典型的。MySQL的写法是LIMIT offset, size,而达梦支持的写法是LIMIT size OFFSET offset,和PostgreSQL风格类似:

-- MySQL SELECT * FROM user ORDER BY id LIMIT 10, 20; -- 达梦 SELECT * FROM user ORDER BY id LIMIT 20 OFFSET 10;

如果你的项目用的是MyBatis PageHelper这种分页插件,插件会根据数据库dialect自动生成分页SQL,前提是你正确配置了达梦的dialect类,否则生成的还是MySQL语法,到达梦执行必报错。

日期函数差异也很大。MySQL的DATE_FORMAT到达梦要改写成TO_CHAR(日期, 'YYYY-MM-DD HH24:MI:SS')。IFNULL可以用NVL或COALESCE替代。GROUP_CONCAT在达梦里要改写为LISTAGG。这些函数在存储过程里高频出现,迁移时做个全局检索,把所有可疑的MySQL函数列出来,逐个改。

自增主键返回值这块,MyBatis的useGeneratedKeys="true"对达梦有效,但业务代码里如果显式调用SELECT LAST_INSERT_ID(),到达梦就要改成序列的currval或达梦的IDENTITY_VAL_LOCAL()函数,逻辑复杂的话建议用ORM层统一处理。

3.3 存储过程、触发器的迁移策略

存储过程和触发器是迁移里的深水区。DTS能自动转换一部分,但转换质量不稳定,我见过转换完的存储过程在达梦里语法能过,一执行就报错的情况。我的实际建议是:导出存储过程语句后,当成普通代码逐个人工改造。

改造时关注三个点。第一是变量声明方式,达梦和MySQL接近,但游标、异常处理块的写法有差异,不能完全照搬。第二是内置函数差异,存储过程里几乎不可避免会用到日期、字符串、聚合函数,这些函数两边的差异非常大,逐条改。第三是schema归属和权限,达梦的存储过程归属于某个schema,执行时要注意schema前缀,应用连库的用户需要有对应执行权限。

触发器这边也一样,核心语法逻辑类似,但触发事件定义和NEW.xxx变量在两侧的类型映射可能不一致,业务赋值时类型不匹配会报错。触发器代码量通常不大,人工改的成本可控,关键是改完要设计几个触发场景,实际触发一次验证逻辑正确性。

4. 实操中踩过的坑与排查实录

4.1 中文乱码,数据导入后全是问号

中文乱码是我遇到最多的问题,前前后后踩过两次。第一次是达梦数据库安装时字符集没有选UTF-8,导致导入的中文全部变成问号,这个基本上只能重建实例优化,重新安装时明确选择UTF-8字符集。第二次是mysqldump导出时没有显式指定字符集,导出的SQL文件用的是MySQL客户端环境的默认编码,如果环境不是UTF-8,文件到达梦端就乱了。解决办法是导出命令加上--default-character-set=utf8mb4,导入之前用文本编辑器打开SQL文件确认编码。控制文件导入CSV时,在dmfldr控制文件里也可以指定字符集,比如CHARACTER SET UTF8,两端保持一致。

4.2 列名撞上SQL关键字,建表一直报错

大量老系统的表结构里,列名直接用group、order、desc、key这种SQL关键字。MySQL对关键字的容忍度比较高,很多场景下不报错,但达梦的语法检查会更严格,建表时直接报错。处理办法有两个:一是在SQL脚本里给敏感列名加双引号,绕开语法检查,但这样后续所有SQL都要带引号,维护起来很痛苦;二是迁移前梳理列名,把关键字列名统一改掉,应用代码里同步修改字段映射。改列名会影响接口返回的字段名,所以要和业务方提前对齐,我实际项目里一般优先选第二种方案,一次性改干净。

4.3 视图依赖导入顺序,报错“对象不存在”

用mysqldump导出视图时,语句顺序是按数据库字典顺序排列的,不是按依赖关系排列的。结果就是视图A依赖视图B,但A的定义排在前面,在达梦执行时先建A,报错B不存在。解决策略是把所有视图脚本单独拆到一个文件,梳理依赖关系,被依赖的视图先执行,逐层往上导入。如果依赖关系复杂,可以先按原顺序跑一遍,把报错的记录记下来,再根据报错信息调整顺序,反复两三次就能跑通。

4.4 大表导入慢到怀疑人生

一开始我用disql直接执行数据SQL脚本导一张300万行的表,结果一个多小时都没跑完,因为每条INSERT都是单独提交事务,开销极大。后来换成dmfldr批量导入,几分钟就搞定。这里还有几个优化技巧:导入之前先删除索引和约束,导入完成后再一次性重建,否则每插一行都要更新索引,速度成倍下降;控制文件的分隔符尽量简单,不要用特别长的字符串,解析开销大;如果是超大表,还可以关闭达梦的归档日志模式,导入完成后重新开启,减少日志写入开销。

4.5 连接层面的SSL与驱动问题

MySQL端如果开启了SSL,mysqldump导出时可能报SSL连接错误,命令里加一个--ssl-mode=DISABLED就能跳过SSL握手。这个参数在MySQL 8.0版本里尤其常见。应用连接达梦时,如果提示找不到驱动类,说明应用里没有达梦的JDBC驱动jar包,驱动在达梦安装目录的driver文件夹下,比如DmJdbcDriver18.jar。连接不上就先用达梦客户端工具手动连一次,确认用户名、密码、端口没问题,再排查应用配置。端口默认是5236,防火墙要放行。Navicat新版也支持连接达梦,可以在连接类型里选择达梦数据库,填好地址、端口、用户名即可,平时开发调试用它看数据也很方便。

5. 迁移后的验证与应用侧适配

5.1 数据一致性三层核对

迁移完成不等于真的完成,数据核对必须做,我一般分三层。第一层是行数核对,每张表SELECT COUNT(*),两边的数字完全一致,这是底线。第二层是抽样核对,挑日期、金额、文本这类敏感字段,随机抽数据逐字段比对。第三层是核心表全量哈希比对,把每一行的所有字段拼成一个字符串,取MD5,两边的哈希值一致才算真正放心。第三层虽然耗时,但核心表跑一遍值得,尤其是有资金、订单、用户数据的库,别省这一步。

5.2 Spring Boot + MyBatis + Druid连接达梦

大部分Java项目迁移完,第一个遇到的就是应用连不上达梦。达梦JDBC驱动类名是dm.jdbc.driver.DmDriver,连接URL格式是jdbc:dm://IP:5236。Spring Boot + Druid的配置大概是这样:

spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: dm.jdbc.driver.DmDriver url: jdbc:dm://192.168.1.10:5236 username: your_user password: your_password druid: initial-size: 5 min-idle: 5 max-active: 20 validation-query: SELECT 1

生产环境别用SYSDBA账号连应用,这是达梦的超管账号,权限过大。新建一个业务账号,只授予所需表的权限,安全性才有保障。

MyBatis项目除了连接配置,还要检查Mapper里的SQL有没有MySQL特殊写法。比如<if test="xxx != null">里面用了MySQL函数,或者分页SQL写死了LIMIT,这些到达梦都可能挂。如果在Mapper XML里的SQL不长,手工改是最直接的;SQL非常多的话,建议做一个SQL静态扫描工具,把MySQL特征语法找出来批量改。MyBatis Plus本身对达梦的支持还不错,注意分页插件要配置正确的dialect。Hibernate用户要特别注意,达梦没有官方Hibernate方言,需要自己配置一个方言类,否则自动生成的SQL可能带MySQL的特征语法,执行时报错。

5.3 达梦日常运维要点

项目上线之后,达梦的运维操作和MySQL差异不小。图形化管理工具是DM管理工具,命令行工具是disql,备份恢复用DMRMAN。日常需要关注的点包括:表空间使用率、数据文件大小、数据库日志报错、活动会话数、慢SQL。达梦慢SQL可以从两个地方看,一是开启SQL日志后分析日志文件,二是查动态性能视图中执行时间长的SQL。备份策略要提前设置好,建议至少每天做一次全量备份,业务高峰时段别做备份,避免影响性能。熟悉了这些基本操作之后,从MySQL迁移到达梦的切换阶段就能平稳度过了。

6. 从一次性迁移走向常态化迁移

6.1 脚本化迁移让三分钟复用

如果整个流程只跑一次,手工操作完全没问题。但现实情况往往是:先把数据迁到测试环境,应用测试发现问题,修复SQL或表结构,然后清掉数据重新迁一遍,反复好几次。这就要把流程脚本化。我的做法是维护一个迁移脚本目录,里面放结构导出脚本、结构改写脚本、数据导出脚本、dmfldr控制文件、导入脚本、校验脚本,每个脚本都是可重复执行的。第一次跑通之后,后面再迁移只需要改数据库连接串和库名,其他代码都不用动,这时候你就真正体会到三分钟迁移的价值了。

6.2 数据中台场景下的持续同步思路

如果做数据中台或异构系统整合,迁移就不是一次性替换那么简单了,而是多个系统间持续的数据同步。这时候除了一次性迁移工具,还需要考虑实时同步链路,比如基于日志的CDC工具,把MySQL的binlog变更实时同步到达梦。相比一次性迁移,持续同步对数据校验的要求更高,要建立周期性的对账机制,保证两端数据最终一致。先把一次性迁移的流程跑顺、脚本化、验证好,做持续同步时就能复用一套标准的校验逻辑,整体的迁移治理体系也会更成熟。

最后再说一个我个人的经验。三分钟完成MySQL到达梦的迁移完全可行,但背后要提前做足功课:导出参数的细节、SQL方言差异、字符集控制、批量加载工具的热练使用,每一项都得提前排查。真正动手之前,强烈建议别拿生产库直接练手,先用最小数据集把全流程跑一遍,把坑都踩完,再对全量数据操作。这套流程跑顺之后你会发现,在两个数据库之间搬数据,本质上就是一套统一的导出、改写、加载、校验动作。难的不是迁移本身,而是对两个数据库特性差异的理解深度。

返回列表