☰
MySQL迁移达梦实战:全流程拆解与高频避坑指南
2026/10/10 7:01:41 网站建设 项目流程

做了这么多年的数据迁移,我越来越觉得这个活儿的关键不在于“搬”的动作,而在于搬之前的规划和搬之后的验证。尤其是把MySQL迁移到达梦这种国产数据库,表面上看起来都是关系型,真动起手来到处都是差异。这篇文章不整虚的,就用实际迁移经验,把从评估、改造、迁移到验证的完整链路拆给你看,照着做能少踩一大半坑。

先给这篇内容定个位:适合正在做数据库国产化替代的DBA、后端开发,以及项目负责人。你会看到数据类型怎么映射、SQL怎么写才不报错、存储过程怎么改才编译得过、大批量数据怎么导才快,以及那些报错信息背后真正的原因。我尽量把每一步的“为什么”也讲清楚,毕竟知其然,才能在自己遇到变体问题时举一反三。

1. 迁移前的全局评估与目标定位

很多人拿到迁移任务,第一反应就是找个工具点几下,把表和数据导过去。这个思路不是不行,而是容易在后期吃大亏。工具能帮你搬走“形”,搬不走“魂”——那些藏在存储过程里的业务逻辑、隐式类型转换、特殊函数用法,才是真正的坑。

1.1 先盘清楚要迁什么

我建议开工之前先做一个对象清单盘点,把源库里的东西分门别类列出来,至少包括这几类:

  • 表结构:表数量、字段数量、索引、约束、自增列、默认值、字符集。
  • 数据:总行数、大表清单、单表数据量级、是否有TEXT/BLOB大字段。
  • 逻辑对象:视图、存储过程、函数、触发器、事件(MySQL的EVENT)。
  • 外部依赖:应用侧SQL中有没有非标准写法、ORM框架生成的SQL是否带方言特性。

这一步的作用是让你对工作量有个准确判断,也方便后续分批次推进。我见过一个项目,表面上只有几十张表,结果一张核心表里全是TEXT字段,单表几个GB,导入时如果不做特殊处理,能跑到怀疑人生。提前把这类“硬骨头”标出来,时间分配就不会失控。

盘完对象之后,还要做一次兼容性抽查。不要等到全量迁移完再测,先挑三五张有代表性的表,把DDL拉出来比对,把几条复杂SQL拿去目标库跑一下,成本低、见效快,能提前暴露大部分方向性问题。

1.2 方案选型:图形工具、脚本迁移怎么选

迁移方案一般有三条路,各有利弊,没有绝对的好坏,关键看场景。

第一条路是用达梦自带的图形迁移工具DTS。它的优势是操作门槛低,能把表结构、数据、视图这些“常规对象”一次性带过去,适合时间紧、对象规整、逻辑对象不复杂的项目。但它的短板也很明显:对存储过程这种高兼容性要求的对象,迁移后经常需要手工改,而且日志信息有限,出了问题不太好定位。

第二条路是手工编写DDL脚本和导入脚本。听着原始,但可控性最强。你可以对每个字段做精确的类型映射,对每个存储过程做逐行改写,还能结合版本管理工具把整个迁移过程脚本化,方便重复执行和复盘。代价就是前期工作量偏大,对实施人员的要求也更高。

第三条路是借助ETL工具做数据层面的同步,比如用通用的数据集成平台。这种方式对异构数据库的大数据量同步比较友好,但通常解决不了存储过程、函数这类代码对象的迁移,只能作为数据搬运的辅助手段。

我个人的建议是组合使用:表结构和数据用DTS做第一轮快速搬运,再用脚本做第二轮修正和补充,逻辑对象全部手工处理。这样既有速度,又有质量兜底。

1.3 目标端环境规划要提前定

环境规划这件事,看似不起眼,实际上决定了后面所有步骤的体验。关键决策点有三个。

第一个是数据库初始化时的兼容模式。达梦支持兼容多种数据库语法,如果源头是MySQL,建议在初始化实例或建库时开启MySQL兼容模式。这样做的好处是部分函数和语法能被直接接受,比如字符串处理、日期函数等。但你要清醒:兼容模式解决的只是“部分语法”问题,不是万能药,复杂逻辑该改写还得改写。

第二个是字符集和大小写敏感性。MySQL的utf8mb4字符集、大小写敏感的排序规则,和达梦的默认设置不一定一致。如果在源头就是utf8mb4,目标库建议也用UTF8类字符集,避免中文乱码和排序错乱。大小写敏感性更要提前定死,因为这是初始化级别的参数,后期想改非常麻烦。

第三个是表空间和用户规划。达梦里用户和模式是绑定的,一个用户对应一个同名模式,这和MySQL的DATABASE概念不同。建议一个MySQL库对应达梦的一个用户/模式,应用连接就用这个用户,这样权限隔离和对象归属都清晰。

2. 两库差异全梳理:这是迁移成败的核心

如果说评估规划是“战前侦察”,那差异梳理就是“战术手册”。MySQL和达梦虽然都是关系型数据库,但底层设计思路不同,导致在数据类型、SQL语法、过程化语言三个方面差异非常大。这一节把常见的差异点全部摊开,建议收藏当字典用。

2.1 数据类型映射:一张表搞定绝大多数建表问题

建表是迁移的起点,数据类型映射错了,后面全得返工。下面是常见MySQL类型到达梦的标准映射方式:

MySQL类型达梦推荐类型说明
TINYINTTINYINT取值范围一致,直接映射
TINYINT UNSIGNEDSMALLINT达梦无UNSIGNED概念,需扩展长度
SMALLINT / INT / BIGINTSMALLINT / INT / BIGINT直接映射,注意UNSIGNED处理
VARCHAR(n)VARCHAR(n)注意长度单位差异,见下方说明
CHAR(n)CHAR(n)直接映射
TEXT / MEDIUMTEXTTEXT / CLOB大字段建议用CLOB,操作更灵活
LONGTEXTCLOB防止超长文本截断
BLOB / LONGBLOBBLOB二进制大对象直接映射
DATETIMEDATETIME直接映射
TIMESTAMPTIMESTAMP注意默认值和时区行为差异
DATE / TIMEDATE / TIME直接映射
DECIMAL(m,n)DECIMAL(m,n)直接映射
FLOAT / DOUBLEFLOAT / DOUBLE直接映射,注意精度表现
BITBIT直接映射
ENUMVARCHAR + CHECK达梦不支持ENUM,用约束兜底
SETVARCHAR建议应用层校验,达梦无SET
JSONTEXT / CLOB达梦有JSON支持但不通用,稳妥用CLOB

这里最需要留意的是VARCHAR的长度单位。MySQL的VARCHAR(n)严格说是字符数,而达梦的VARCHAR长度单位存在字节和字符两种口径,取决于初始化参数设置。如果在UTF8字符集下,一个汉字在MySQL里算1个字符,到达梦按字节算就是3个字节。原表VARCHAR(100)能存100个汉字,达梦如果按字节可能只能存33个。这会导致两个问题:一是数据导入时报“字符串长度超出”,二是同样的数据存储容量下降。规避方法是在建库时确认长度单位口径,或是在生成DDL时按业务需求放大长度。

ENUM和JSON这两个类型要单独说。ENUM在MySQL里是个省事的东西,但达梦没有对应类型,最稳的做法是建成VARCHAR并加CHECK约束,由数据库保证取值合法。JSON类型也一样,除非你的达梦版本明确支持JSON功能,否则一律用CLOB存原始JSON字符串,序列化和解析交给应用层。牺牲一点查询便利,换来的是一劳永逸的兼容性。

2.2 SQL语法差异与改写清单

数据类型是“表”的层面,SQL语法则是“查询”的层面。应用的绝大部分SQL都要经过这关,改写量常常是最大的。我挑几个必踩的点讲。

分页语法是最高频的差异。MySQL的LIMIT m,n写法到达梦这里行不通,达梦支持的是TOP、ROWNUM以及标准SQL的FETCH。只取前N条时用SELECT TOP N * FROM t就行;真正翻页时,推荐用ROWNUM包一层子查询:

-- MySQL写法 SELECT * FROM t ORDER BY id LIMIT 0, 10; -- 达梦写法 SELECT * FROM ( SELECT t.*, ROWNUM rn FROM ( SELECT * FROM t ORDER BY id ) t WHERE ROWNUM <= 10 ) WHERE rn >= 1;

不同的达梦版本支持的语法有细微差别,有的新版本也能直接识别LIMIT,但为了兼容性,我建议按上面的ROWNUM写法来,它在各个版本里都稳定。

函数替换是第二大类。IFNULL要换成NVL;DATE_FORMAT换成TO_CHAR,格式串从%Y-%m-%d变成YYYY-MM-DD;GROUP_CONCAT换成LISTAGG;多参数CONCAT如果遇到只支持两个参数的版本,要嵌套或改用||连接符。这些替换看起来小,但如果应用里写了几百处,逐个手工改就要命了。所以我在前面强调要先做兼容性抽查,目的就是提前评估这个改写量。

还有一个容易忽视的是标识符处理。MySQL习惯用反引号包裹库名和表名,达梦不支持反引号,需要去掉,必要时改成双引号。另外MySQL表名字段名默认大小写不敏感,而达梦对未加引号的标识符统一按大写存储,这在多数情况下没影响,但如果你应用里有大小写敏感的字符串比较,就要留意。

INSERT冲突处理的差异也很大。MySQL的INSERT ... ON DUPLICATE KEY UPDATE在达梦不支持,需要改写为MERGE:

MERGE INTO t1 USING (SELECT #{id} AS id, #{name} AS name FROM DUAL) t2 ON (t1.id = t2.id) WHEN MATCHED THEN UPDATE SET t1.name = t2.name WHEN NOT MATCHED THEN INSERT (id, name) VALUES (t2.id, t2.name);

这种改写在数据同步类业务里非常常见,建议在写工具类SQL时提前统一成MERGE风格,省得后来单独返工。

2.3 存储过程与触发器的兼容性改造

存储过程是迁移里最费神的部分,因为MySQL的过程语言和达梦(兼容Oracle风格)在骨架上有本质差异。我总结了几个高频改造点。

变量声明的位置不同。MySQL允许在BEGIN...END内部中途声明变量,达梦习惯把所有变量集中声明在DECLARE区域。赋值语句也不同,MySQL用SET或者SELECT INTO,达梦用:=赋值。举个例子,一个最简单的变量逻辑:

-- MySQL风格 BEGIN DECLARE v_cnt INT; SELECT COUNT(*) INTO v_cnt FROM t; SET v_cnt = v_cnt + 1; END; -- 达梦风格 DECLARE v_cnt INT; BEGIN SELECT COUNT(*) INTO v_cnt FROM t; v_cnt := v_cnt + 1; END;

看着差别不大,但存储过程一长,这种细节会反复触发编译错误。

异常处理机制完全不是一个套路。MySQL用DECLARE CONTINUE HANDLER FOR NOT FOUND来捕获游标结束,达梦则用EXCEPTION块和内置异常名NO_DATA_FOUND。游标循环的写法也要相应调整,达梦更常用FOR循环直接遍历结果集。

自增列在存储过程中的取值方式同样要改。MySQL用LAST_INSERT_ID()拿刚插入的ID,达梦一般用IDENTITY_VAL_LOCAL(),或者使用序列的CURRVAL。如果源逻辑里大量依赖这个函数,趁迁移时统一改成序列方案,后面维护起来反而省心。

这里我提一个经验性的忠告:存储过程迁移不要追求逐字翻译,而是先看懂原逻辑,再用达梦的习惯重新写一遍。逐字翻译的产物往往又丑又难调试,重写虽然费点时间,但长远看可维护性高得多。

3. 实操全流程逐步拆解

讲完理论,进入实操。我会按真实项目的推进节奏,从建库到验证一步步走,每一步给出可直接落地的做法。

3.1 目标库、用户与模式准备

第一步是在达梦里创建业务用户和模式。达梦的语法和Oracle接近,创建一个用户后会自动产生同名模式。比如要迁移一个名为order_db的MySQL库,到达梦就是创建order_db用户:

CREATE TABLESPACE order_ts DATAFILE 'order_ts.dbf' SIZE 1024M AUTOEXTEND ON NEXT 128M; CREATE USER order_db IDENTIFIED BY "your_password" DEFAULT TABLESPACE order_ts; GRANT DBA TO order_db;

单独建表空间再指定给用户,好处是数据文件位置可控,后续备份恢复都清楚。如果你不差这一步,直接建用户用默认表空间也行,但大表项目强烈建议规划独立表空间,防止默认表空间被塞爆。

还需要确认兼容参数是否已经生效。可以执行下面这类查询确认当前实例的兼容模式相关配置:

SELECT * FROM V$DM_INI WHERE INI_NAME LIKE '%COMPATIBLE%';

如果在建库时没开MySQL兼容模式,而项目里又有大量MySQL方言SQL,可以考虑调整对应兼容参数,但要注意有些参数是静态的,需要重启实例才生效。所以最好在迁移前就定好,避免中途切换。

3.2 表结构迁移的两种落地方式

表结构迁移我建议按“先用DTS拖一遍,再用脚本修”的节奏走。

用DTS迁移表结构时,勾选好源库连接、目标库连接、需要迁移的表,工具会自动生成建表语句并执行。这个过程很快,基本不用干预。但工具生成的DDL通常有几个问题:数据类型映射是按默认规则来的,不一定最优;注释可能丢失;索引命名可能不符合你的规范。所以DTS跑完后,一定要抽样检查几类表:含大字段的表、含自增列的表、含特殊默认值的表。

对于需要手工控制的核心表,我习惯直接写DDL。拿一张用户表举例:

-- MySQL原始表 CREATE TABLE `t_user` ( `id` INT NOT NULL AUTO_INCREMENT, `username` VARCHAR(50) NOT NULL, `email` VARCHAR(100) DEFAULT NULL, `status` ENUM('ACTIVE','DISABLED') DEFAULT 'ACTIVE', `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 对应达梦表 CREATE TABLE t_user ( id INT IDENTITY(1,1) NOT NULL, username VARCHAR(200) NOT NULL, email VARCHAR(200) DEFAULT NULL, status VARCHAR(20) DEFAULT 'ACTIVE', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, CONSTRAINT pk_t_user PRIMARY KEY (id), CONSTRAINT ck_t_user_status CHECK (status IN ('ACTIVE','DISABLED')) ); CREATE INDEX idx_username ON t_user(username); COMMENT ON COLUMN t_user.username IS '用户名';

这里有两个值得注意的处理。一是VARCHAR长度我按字符数估算后放大了,避免因字节口径不同导致长度不够。二是ENUM改成了VARCHAR加CHECK约束,业务取值合法性仍然由数据库把关。索引的命名规范也可以趁这次迁移重新梳理,毕竟MySQL下经常出现一堆idx_开头的冗余索引,迁移是清理的好时机。

3.3 数据迁移:从“能导入”到“导得快”

表结构就位后开始导数据,这个阶段的目标就两个字:快、稳。

如果表不多、数据量在几十GB以内,DTS的数据迁移功能足够用。配置好源和目标连接,选择对应表,工具会自动批量读取写入。但如果遇到超大表,直接全表SELECT再逐条INSERT会非常慢,而且容易造成目标库事务日志膨胀。

数据量大的时候,我推荐用达梦的命令行批量装载工具dmfldr,类似Oracle的SQL*Loader。先导出一份文本格式的数据文件,再用dmfldr快速装载,速度比逐条INSERT快一个量级。控制文件示意如下:

LOAD DATA INFILE 't_user_data.txt' INTO TABLE t_user FIELDS TERMINATED BY '|' TRAILING NULLCOLS ( id, username, email, status, created_at )

执行装载命令时,可以指定批大小和并行度,比如:

dmfldr userid=order_db/your_password control=\'t_user.ctl\' data=\'t_user_data.txt\' batch=10000 parallel=4

实操中有个细节:如果目标表有IDENTITY自增列,默认情况下不允许直接插入指定值。如果业务需要保留原ID值,最常见做法是建表时不设置IDENTITY,字段用普通INT,数据导入后再用序列加触发器模拟自增。如果版本支持显式插入IDENTITY列,也要在配置里开对应开关,请根据你用的版本文档确认。千万别在导入中途才发现写不进ID,那种返工很熬人。

导入过程中,建议分批提交,一批一万到两万条比较稳妥。同时关掉目标表的约束和索引,导完再重建。这样能显著缩短时间,也避免约束冲突在导入中反复打断流程。等数据全部落库后,再按依赖顺序重建外键和触发器。

3.4 逻辑对象迁移与迁移后验证

数据和表结构都完成了,轮到视图、存储过程、函数、触发器这些逻辑对象。视图迁移相对简单,重点检查SQL语法差异,把MySQL的函数替换成达梦写法就行。存储过程和函数就需要逐个编译、逐个调试了。

达梦里编译存储过程的常见方式是:

CREATE OR REPLACE PROCEDURE proc_name AS ...

创建成功后,还要用下面的语句再检查一下状态:

SELECT OBJECT_NAME, STATUS FROM USER_OBJECTS WHERE OBJECT_TYPE = 'PROCEDURE';

凡是STATUS不是VALID的对象,都要进去看具体编译错误。达梦提供了系统视图查编译错误,比如USER_ERRORS,根据错误行号定位修改即可。

迁移后验证是“高质量”这部分的真正体现。我建议至少做三层验证。

第一层是对象数量验证:比较源库和目标库的表数量、视图数量、存储过程数量,确保一个不少。

第二层是数据一致性验证。对每张表比对行数,最笨也最可靠的办法是COUNT(*)对账:

SELECT COUNT(*) FROM t_user;

再加一层特征值比对,比如对关键数值字段做SUM,对时间字段做MAX/MIN,防止行数一致但内容错位。

第三层是业务功能验证。把应用切到目标库连接,跑一遍核心链路,比如登录、下单、查询列表这类高频操作。这一步本质上是做“SQL方言验收”,因为很多语法问题只有在真实业务SQL里才会暴露。

我自己的习惯是把这层验证做成一份测试清单,覆盖所有核心业务场景,每项标记通过/失败,留档备查。别嫌麻烦,这份东西对验收、审计和后续排障都有用。

4. 常见报错与排查技巧实录

迁移过程中报错是常态,不报错才不正常。下面这些是我在多个迁移项目里反复遇到的高频问题,直接把排查思路和解决路径写出来,希望帮你少走弯路。

4.1 表结构阶段的高频报错

“无效的列名”和“无效的表名”往往不是真的对象不存在,而是大小写或标识符问题。达梦对未加引号的标识符统一按大写存储,如果应用里用了混合大小写且加了引号,可能就找不到了。排查时先用USER_TAB_COLUMNS查询确认实际对象名,再决定是改SQL还是重建对象。

“字符串长度超出”是VARCHAR口径问题的高频表现。尤其在UTF8字符集下,VARCHAR(100)按字节算只能存约33个中文汉字。解决方向有两个:一是确认建库参数里长度单位是字符;二是把目标表的VARCHAR长度按3倍预留。具体用哪种,取决于你的参数设置,但别在DTS生成的DDL上盲目自信,一定要抽查中文场景。

还有一种是“不是GROUP BY表达式”之类的分组报错。MySQL在关闭ONLY_FULL_GROUP_BY时对分组查询很宽松,达梦的默认行为可能更严格。遇到这种就调整查询,把非聚合列要么加进GROUP BY,要么用聚合函数包一层。

4.2 数据导入阶段的高频报错

乱码问题几乎每次都有人遇到。根源基本是源库导出文件字符集与目标库字符集不一致。导出时强制指定字符集,比如UTF8,导入时也声明同样的字符集,能解决大部分乱码。如果已经乱码,先确认数据是导入时就丢了还是显示层的问题,再对症下药。

主键或唯一键冲突,常见于重复执行导入脚本。解决方法是导入前先TRUNCATE目标表,或使用MERGE方式导入。如果数据量大又需要反复试错,写一个TRUNCATE + IMPORT的二合一脚本,效率会高不少。另外要检查自增列的处理,重新导入时如果不重置序列,后续插入的主键可能会和存量数据冲突。

“数字溢出”也经常出现,原因是MySQL的UNSIGNED列映射成了达梦的带符号列。例如INT UNSIGNED最大能到42亿,而达梦INT最大只有21亿。这种字段在建表时就要把映射逻辑想清楚,升级到BIGINT或让应用侧接受取值范围调整。

4.3 运行期SQL兼容问题

最麻烦的是那种“表建好了、数据导进去了、应用一跑就报错”的情况。常见报错有:函数不存在、ORA-like语法错误、字符串比较行为不同。

函数不存在的排查办法很直接:把报错里的函数名拿到达梦文档里查。IFNULL、DATE_FORMAT这类函数在部分兼容模式下可用,但通用性不如NVL和TO_CHAR。想一劳永逸,就把应用SQL里的MySQL专有函数全部统一替换成Oracle风格写法,虽然前期工作量大,但后续不会再被这个事反复纠缠。

字符串比较行为的不同很隐蔽。MySQL的VARCHAR比较默认不区分大小写,这可能让达梦的默认区分大小写行为在登录、去重等场景中与预期不符。遇到这种情况,用LOWER或UPPER包一层再比较是最直接的解法。

4.4 性能问题与参数调整

迁移完成后性能不达标,也要按几类原因去排查。

索引问题最容易被忽略。数据导入时为了速度关掉的索引,如果忘了重建,或者DTS工具生成索引失败,应用查询就会全线变慢。用系统视图查一下目标库索引数量,和源库做对比,这一步一分钟就能完成。

统计信息过期也会导致执行计划错乱。迁移后第一件事就是刷新统计信息:

DBMS_STATS.GATHER_TABLE_STATS('ORDER_DB', 'T_USER', CASCADE => TRUE);

统计信息一刷新,很多“说不清为什么慢”的SQL会自然恢复正常。

还有内存和并发参数。达梦实例的BUFFER大小、最大会话数、并行度等参数需要根据业务量调整。数据迁移刚完成时,先按源库的业务量级别设置一个合理基线,再通过压力测试逐步调参,不要一上来就拉满。

最后提一个很容易踩的实操坑:达梦的执行计划查看方式和MySQL不完全一样,排查慢SQL时要习惯用达梦的EXPLAIN格式和性能视图,别拿MySQL的思维方式硬套,否则会绕很多弯路。

写在最后

这几年做过的迁移项目,从几十张表到上千张表的都经历过。我最大的体会是:数据库迁移这件事,工具只能帮你完成前20%的工作,剩下的80%靠的是对两套数据库差异的深刻理解,以及一个严谨的验证流程。

如果你现在正准备启动一个MySQL到达梦的迁移,我建议你把重心放在“评估”和“验证”这两个环节上。评估做得越细,迁移中的意外越少;验证做得越严,上线后的风险越小。至于迁移工具本身,反而不用太纠结,DTS也好、dmfldr也好,上手都很快,真正拉开差距的是你对数据字典、SQL改写和存储过程调试的熟练程度。

最后再分享一个小技巧:整个迁移过程,务必保留一份完整的操作记录,包括DDL脚本、导入命令、参数调整项、每个阶段的耗时和报错处理方式。这份记录在迁移验收、问题回溯、甚至后续二次迁移时,价值远超你的想象。别嫌麻烦,养成这个习惯,比任何工具都管用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询