先说结论:MySQL 到达梦的迁移,真正让人头疼的往往不是数据量,也不是迁移工具跑得慢,而是那些平时写惯了、跑得好好的 SQL,到达梦上突然就报语法错误。我最近刚好完整跟完了一个从 MySQL 5.7 迁到达梦 DM8 的项目,涉及几百张表、上千条存储过程和视图,中间踩了不少坑,也积累了一套相对成熟的迁移方案。这篇文章就围绕 SQL 语法差异和整体迁移路径展开,把遇到的典型问题、排查方法、实操步骤都整理出来,希望对准备做同类迁移的团队有实际帮助。
1. 迁移前必须想清楚的三件事
1.1 明确迁移目标与范围
很多人接到“数据库国产化替换”任务后,第一反应是赶紧装达梦、然后把表导过去,结果越到后面越乱。我建议先做一次范围梳理,至少要回答这几个问题:迁移的是生产库还是测试库?涉及哪些业务模块?迁移后是彻底下线 MySQL,还是并存一段时间做双跑?有没有自研框架或第三方系统需要同步适配?
这个阶段最容易被忽略的是“范围界定”。比如我们项目里,有一张报表表被三个系统同时读写,其中一个老旧系统已经没人维护,但它每天还会跑定时任务写入数据。如果迁移范围只按新业务系统来划,这张表就会漏掉,等上线后才发现数据对不上,再回头补就很被动了。所以第一个动作应该是盘点所有数据源、所有消费者,以及每张表的读、写、删频率,而不是直接进 SQL 改造环节。
另一个重点是区分“一次性迁移”和“持续性同步”。如果目标系统在上线后要和源系统并行运行一段时间,就必须设计增量同步机制。这个需求会直接影响迁移工具选型和数据校验方案,不能等到迁完再想。
1.2 兼容性评估怎么做
兼容性评估是迁移前最重要、也最容易被高估的一步。很多人以为把数据库实例初始化成 MySQL 兼容模式就万事大吉,实测下来并不是这么回事。达梦的兼容模式确实能识别一部分 MySQL 语法,比如 LIMIT、AUTO_INCREMENT、反引号等,但像存储过程、函数、触发器这些复杂对象,仍然会有大量细节差异。
所以我建议评估分三层来做:
- 结构兼容性:把所有表结构、视图、存储过程、触发器、序列、自定义函数的 DDL 提取出来,在达梦环境里挨个执行,记录报错对象。这一步能直观暴露大部分语法差异。
- SQL 兼容性:从 MySQL 的慢查询日志、审计日志或数据库层面抓取一段时间内的真实 SQL,去掉重复后放到达梦库里跑一遍,统计失败率。重点看高频 SQL,而不是那些一年都跑不了几次的冷门语句。
- 应用层兼容性:检查应用里的 ORM 映射文件、MyBatis XML、JPA 注解、JdbcTemplate 调用,凡是手写 SQL 的地方都要过一遍。很多问题不是出在数据库层,而是出在框架自动生成的 SQL 方言上。
三层评估做完后,基本能列出一份“改造工作量清单”。如果发现自己系统里 SQL 写法非常统一,比如所有分页都走同一条公共方法,那改造压力就小很多。反之,如果到处散落着各种手写 SQL,就要把工期预算放宽。
1.3 迁移工具与方案选型
迁移工具的选型没有绝对标准,关键看数据量、表数量、停机窗口和团队熟悉度。我列一个对比表,方便你根据自己的场景选:
| 工具/方案 | 适合场景 | 优点 | 需要留意的坑 |
|---|---|---|---|
| 达梦自带 DTS 迁移工具 | 中小数据量、表结构简单 | 图形化界面,操作简单,能直接迁移表结构和数据 | 大表容易超时,类型映射需要人工检查 |
| DataX 离线同步 | 大数据量、需要并行控制 | 分片控制灵活,断点续传,吞吐量高 | 需要写 JSON 配置,达梦 Writer 要额外引入驱动 |
| mysqldump 导出 + 脚本改造 DDL | 纯结构迁移、SQL 可控性要求高 | 所有改动都能审阅,比较透明 | 文本替换工作量大,容易遗漏特殊写法 |
| 自研 JDBC 分批导入 | 数据量大且有定制清洗逻辑 | 能嵌入业务清洗逻辑,进度可控 | 开发成本高,不如成熟工具省事 |
我们这次选的是“DTS 迁移表数据 + 脚本批量改造 DDL”的组合。理由很简单:临时停机窗口只有 4 个小时,全量数据 1.2TB,用 DTS 做初迁,再用脚本改造的对象清单处理结构差异,最后用自研校验工具做一致性比对。如果你也打算组合使用,我建议提前做一轮全流程演练,别等到上线当晚第一次跑完整链路。
2. 达梦与 MySQL 的 SQL 语法差异清单(踩坑实录)
2.1 建表语句的差异:自增列、注释、默认值
建表差异是最先暴露的问题,因为只要一执行 DDL 就能看到报错。最容易中招的几个点逐个说。
自增列在 MySQL 里写id INT AUTO_INCREMENT,达梦原生写法是id INT IDENTITY(1,1)。如果实例开启了 MySQL 兼容模式,AUTO_INCREMENT 也能识别,但要注意 IDENTITY 在建表语句里的位置限制,它必须跟列定义放一起。还有一种做法是建普通表之后,用序列加触发器模拟自增,这种方式更接近 Oracle 习惯,但我不推荐在迁移初期引入,因为每一张表都要配触发器和序列,维护成本太高。兼容模式下能直接用的就用AUTO_INCREMENT。
默认值这块坑最多。MySQL 很常见的写法:
created_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP达梦支持DEFAULT CURRENT_TIMESTAMP,但对ON UPDATE CURRENT_TIMESTAMP支持不理想,至少在 DM8 的某些版本里会报语法错误。处理方法很简单:去掉ON UPDATE,把这个逻辑挪到应用层,或者在后端代码里统一维护update_time。如果团队不想改代码,也可以做一个触发器来更新这个字段,但我的建议是尽量别为这种小功能引入复杂性。
再一个是注释。MySQL 建表经常写在列后面:
`user_name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '用户名'达梦在建表语句里也支持 COMMENT,但如果你用脚本批量替换的时候,遇到特殊字符、中文引号,很容易出现语句中断。更稳妥的方式是建表后再执行:
COMMENT ON COLUMN user_info.user_name IS '用户名'; COMMENT ON TABLE user_info IS '用户信息表';但这样表注释和列注释要分开处理,批处理脚本里要拆成两条语句。如果你走的 DTS 迁移工具,它会帮你转换,手写脚本就要特别注意。
还有建表尾巴上的那串东西,MySQL 里很多人写ENGINE=InnoDB DEFAULT CHARSET=utf8mb4,达梦直接不支持,脚本替换时整段删掉就好。另外CHARSET、COLLATE在达梦里没有一一对应的概念,删了不影响使用,但应用程序如果依赖排序规则,后续要单独测试。
2.2 查询语句的差异:分页、函数、布尔值
查询语句的差异在迁移后影响范围最大。先说分页,MySQL 的经典写法:
SELECT * FROM user_info ORDER BY id DESC LIMIT 10, 20;达梦如果开了 MySQL 兼容模式,LIMIT 一般能跑,但有的版本对LIMIT offset, count这种写法识别不稳定,尤其是和 ORDER BY 子句配合的时候。我在实际项目中遇到过的报错是“语法分析出错”或“不必要的表”。稳妥做法是统一改成达梦官方推荐的模式:
SELECT * FROM ( SELECT t.*, ROWNUM rn FROM (SELECT * FROM user_info ORDER BY id DESC) t WHERE ROWNUM <= 30 ) WHERE rn > 10;这种写法虽然啰嗦,但任何达梦版本都稳定。如果项目里分页是集中封装了,改动成本很小;如果到处是散落的 LIMIT,建议趁迁移统一收敛到一个公共方法里,这也算迁移带来的额外收益。
函数差异是另一个重灾区。我整理了一个高频替换清单:
| MySQL 写法 | 达梦推荐写法 | 备注 |
|---|---|---|
| IFNULL(expr1, expr2) | NVL(expr1, expr2) | 兼容模式可能支持 IFNULL,但不建议依赖 |
| GROUP_CONCAT(col SEPARATOR ',') | LISTAGG(col, ',') | 注意 LISTAGG 对返回长度和去重要求不同 |
| DATE_FORMAT(date, '%Y-%m-%d') | TO_CHAR(date, 'YYYY-MM-DD') | 格式符写法完全不一样 |
| STR_TO_DATE(str, '%Y-%m-%d') | TO_DATE(str, 'YYYY-MM-DD') | 注意格式符差异 |
| CONCAT_WS(',', a, b) | a | |
| IF(expr, v1, v2) | CASE WHEN expr THEN v1 ELSE v2 END | 达梦没有 IF 函数,用 CASE 最通用 |
| SUBSTRING_INDEX(str, ',', n) | 用 SUBSTR + INSTR 组合改写 | 没有同名单函数 |
以SUBSTRING_INDEX为例,MySQL 里一句就能截取逗号分隔的某一段,达梦里没有这个内置函数,我当时是写了一个存储函数模拟,按分隔符循环拆分。但如果业务里用到的地方不多,我更建议在应用层处理,别在 SQL 层硬抗。
布尔值的差异也要盯一下。MySQL 里BOOLEAN其实就是TINYINT(1),很多人直接写WHERE status = 1。达梦兼容模式支持 BOOLEAN 类型和 TRUE/FALSE 关键字,但如果你在存储过程里用IF (flag = TRUE),不同版本的解析可能不一样。我的建议是,所有布尔判断统一用WHERE flag = 1或者WHERE flag <> 0,避免依赖关键字。
2.3 存储过程与触发器的差异
存储过程差异是最容易让老手翻车的地方,因为它在建库阶段可能不报错,一调用才发现逻辑不对。MySQL 的存储过程写法比较自由,变量可以在 BEGIN 块内随时声明,而达梦更接近 Oracle 风格,声明和主体是分开的。
MySQL 常见写法:
DELIMITER // CREATE PROCEDURE p_get_user(IN v_id INT) BEGIN DECLARE v_name VARCHAR(64); SELECT user_name INTO v_name FROM user_info WHERE id = v_id; IF v_name IS NULL THEN SELECT 'not found' AS msg; END IF; END// DELIMITER ;达梦推荐写法:
CREATE OR REPLACE PROCEDURE p_get_user(v_id INT) AS v_name VARCHAR(64); BEGIN SELECT user_name INTO v_name FROM user_info WHERE id = v_id; IF v_name IS NULL THEN PRINT 'not found'; END IF; END;几个关键差异:达梦的DECLARE段放在AS和BEGIN之间,变量声明必须集中在此处,不能在 BEGIN 后再临时声明;MySQL 里SELECT 'not found'直接返回结果集,达梦存储过程里没有这种直接返回结果集的写法,要返回数据集得用游标或临时表;异常处理机制也不同,MySQL 用SIGNAL SQLSTATE '45000',达梦更常见的是RAISE_APPLICATION_ERROR(-20001, 'msg')。
游标这块差异也很大。MySQL 里游标用DECLARE cur CURSOR FOR SELECT ...,然后OPEN、FETCH、CLOSE;达梦同样有这套语法,但更推荐用 FOR 循环隐式游标,写起来简洁很多:
FOR rec IN (SELECT id, user_name FROM user_info) LOOP -- 直接使用 rec.id、rec.user_name END LOOP;如果你从 MySQL 迁过来,我建议不要一次性重写所有存储过程,而是先按调用频率排序,只改那些真正被业务调用的。很多备份用的历史存储过程可能一年都不跑一次,可以先留着不迁,后面再补。
触发器迁移相对简单,但也容易踩关键字冲突。达梦的触发器语法是:
CREATE OR REPLACE TRIGGER tri_user_info_bi BEFORE INSERT ON user_info FOR EACH ROW BEGIN :NEW.created_at := SYSDATE; END;MySQL 里用NEW.created_at,达梦里要写成:NEW.created_at,注意那个冒号不能掉。如果触发器里修改了NEW字段,达梦要求必须声明FOR EACH ROW且触发器类型是行级触发器,MySQL 侧如果写的是FOR EACH ROW,这块倒是一致的。
2.4 关键字与特殊符号的坑
关键字冲突是迁移中很隐蔽的一类问题,它不像是函数差异那样能看到明确的“不支持”,而是建表或查询时报一个很莫名的错。MySQL 的保留字和达梦的保留字集合并不一致,很多在 MySQL 里能随便用的字段名,到了达梦就成了保留字。
我实际遇到过几个:comment、desc、rank、row、window、groups、condition、module、sort。其中comment和desc最坑,因为业务表里经常有“备注”和“排序”字段。如果你在建表时报语法错误,或者查询时报“不是有效的列名”,可以先怀疑字段名是不是达梦保留字。
处理方法有两种:一种是给字段名加双引号,比如"comment",这能解决问题,但有副作用——双引号内的内容会变成大小写敏感。比如达梦建了一张表,列名为"UserName",那你查询时也得写SELECT "UserName" FROM t,写USERNAME反而找不到。另一种是把字段直接改名,比如comment改成remark,然后在应用层做映射,这个最干净,但需要改代码。我的建议是提前生成一份冲突清单,让业务方决定哪些字段值得改名,哪些用双引号兼容方案。
还有一个细节是 MySQL 的反引号。SELECTuser_idFROM t这种写法在达梦兼容模式里不一定报错,但也别高兴太早,某些版本里反引号只能用于 MySQL 兼容实例,而且与双引号混用时行为不一致。最省事的做法是迁移第一步就把所有反引号去掉,或者统一替换成双引号,别让同一个对象在不同句子里有两种引用方式。
另外,字符串连接符号也要小心。MySQL 里'a' + 'b'是数值相加,会得到 0,而达梦继承了 Oracle 风格,'a' || 'b'才是字符串连接。如果你原来的代码里用了CONCAT函数,达梦基本支持;但如果你看到过name + 'xxx'这种写法,迁移后一定会出问题,因为结果完全不是预期。
3. 一套可落地的迁移实操流程
3.1 环境准备与兼容参数设置
达梦安装完成之后,第一步不是急着建表,而是把初始化参数设置好。达梦在初始化实例的时候会问你兼容模式、字符集、页大小、大小写敏感等问题,这几个选项后面想改非常麻烦,所以一开始就要想清楚。
兼容模式建议直接选 MySQL 兼容。这个选项会直接影响 LIMIT、AUTO_INCREMENT、反引号等语法是否能被识别。我第一次没选对,结果建表阶段就报错,最后只能重新初始化实例重来一遍。
字符集这块,如果原 MySQL 是 utf8mb4,达梦这边也选 UTF-8。注意这里有个参数叫“LENGTH_IN_CHAR”,直接影响 VARCHAR 长度是按字符算还是按字节算。MySQL 的VARCHAR(255)按字符算,255 可以存 255 个汉字;达梦如果不把 LENGTH_IN_CHAR 设为 1,VARCHAR(255)就是 255 字节,中文可能只能存 80 多个字符。这个问题在数据迁移时最容易暴露,插入阶段直接报“字符串超长”,排查起来还得翻数据库参数,特别容易懵。所以初始化时一定把这个参数确认好,或者在 DDL 里人工把长度按字节估算扩一下。
大小写敏感参数也要评估。MySQL 在 Windows 上表名不区分大小写,Linux 上区分,而达梦默认大小写敏感,未加引号的标识符会统一转成大写。如果应用代码里平时就喜欢混用大小写,建议建库时就统一成大写风格,并在应用代码里做好约定。最怕的就是表结构是小写,应用代码里也用小写,但客户端查询工具自动加了大写,几层叠加之后报“表或视图不存在”,非常难排查。
页大小建议根据数据特征选 8K 或 16K。如果单表数据量大、单行数据长,页大小选大一点能减少行链接和 IO 次数。这个参数在建库后不可修改,所以别随便选默认值。
3.2 结构迁移:DDL 改造与脚本化处理
如果是全新迁移,我建议用达梦 DTS 先把所有表结构拉过来,再对照错误清单手动修。这样比从零手写 DDL 要快很多,因为 DTS 会自动处理一部分类型映射。但 DTS 生成的结果不能直接当成最终结构,一定要逐段核对自增列、默认值、注释、字符集这几类信息。
等 DTS 建出基础结构后,再用脚本在源库导出原始 DDL 做差异对比。我们的做法是:mysqldump 导出全部建表语句,然后写一个 Python 脚本做文本替换。替换规则大概是:
- 去掉
ENGINE=InnoDB DEFAULT CHARSET=utf8mb4等尾部设置; - 把
AUTO_INCREMENT替换为IDENTITY(1,1)(兼容模式下也可以保留); - 删除
ON UPDATE CURRENT_TIMESTAMP; - 把反引号去掉或替换为双引号;
- 展开
COMMENT为独立的 COMMENT ON 语句; - 检查所有列名、表名是否命中达梦保留字清单。
这个脚本第一次跑完,产出会有一堆报错,不要慌,这是正常现象。真正的难点不在自动化替换,而在人工修复剩下的特殊写法。比如 MySQL 里有的表用了timestamp(3)这种带精度的类型,达梦要用TIMESTAMP(3),写法就差一个大小写,但有的版本会报错,有的不报。最好的办法是准备一套 DDL 执行回归用例,每次改完脚本都在新的空库上跑一遍建库建表,确认全部对象能完整重建,再进行数据迁移。
3.3 数据迁移:分批、校验、不丢数据
数据迁移环节的核心原则是两条:分片控制、可重跑。我用 DTS 迁初迁时,发现超过 500 万行的表它跑起来很吃力,经常报连接超时或事务回滚。后来调整策略,改成自研 JDBC 分批导入,按照主键范围切分,每批 2000 行左右,用批量 INSERT 提交。
批量插入的达梦写法不要用 MySQL 那种多个 VALUES 逗号拼接,虽然达梦兼容模式可能支持,但超过一定行数报错概率高。我最终用的是 JDBCrewriteBatchedStatements=true参数加上PreparedStatement.addBatch(),这种方式在达梦驱动下表现更稳定,导入速度也快很多。
还有一个细节:导入前先禁用目标表的约束和索引。如果目标表已经有唯一索引、主键、外键,导入过程中每插入一行都要做完整性检查,速度会慢很多倍。我们当时的做法是:导入前删除所有二级索引和约束,全部导完后再重建。但要注意,重建大数据量表的索引本身也要时间,需要算进停机窗口里。
数据校验这边要设计一个独立于业务查询的比对方法。MySQL 和达梦没有完全一致的 checksum 函数,我就用了一个取巧的办法:对每张表按主键字段拼一个文本串,计算 MD5 或 CRC32,再对总数做聚合比对。具体SQL类似:
SELECT COUNT(*), NVL(SUM(CRC32(CONCAT(id, user_name, created_at))), 0) FROM user_info;两边分别跑出来比对结果。这个方案对文本大字段不友好,CLOB 类型参与拼接会失败,所以特殊表单独处理。总体来说,只要总行数一致、抽样字段的值一致,再结合应用层的业务冒烟测试,基本可以交付。
3.4 业务适配与应用切换
数据迁完不意味着项目结束,应用层适配往往是工作量最大的部分。首先改数据源配置:
spring.datasource.driver-class-name=dm.jdbc.driver.DmDriver spring.datasource.url=jdbc:dm://192.168.1.10:5236 spring.datasource.username=DM_USER spring.datasource.password=123456Maven 依赖也要换成达梦 JDBC:
<dependency> <groupId>com.dameng</groupId> <artifactId>DmJdbcDriver18</artifactId> <version>8.1.2.192</version> </dependency>然后集中审查 MyBatis XML。我建议全局搜这几个关键词:LIMIT、GROUP_CONCAT、IFNULL、DATE_FORMAT、CONCAT_WS、反引号、ON UPDATE,一个文件一个文件过。这个步骤不能偷懒,因为 XML 里的 SQL 往往是最复杂的,而且不像代码那样有编译检查,写错了只有跑到了才会报。
事务隔离级别和锁行为也要测试。达梦的默认隔离级别和 MySQL 的可重复读是否存在差异,取决于具体版本和参数设置,建议在压测环境里验证一下高并发下是否有死锁、锁等待超时等问题。我们在测试阶段就遇到过一个批量更新场景,MySQL 下 5 秒跑完,达梦下直接锁超时,后来发现是批量更新条件里涉及了不同类型字段的比较,索引没有命中,锁的范围比预想大得多。这种问题靠改 SQL 解决,不是靠调数据库参数。
4. 迁移过程中高频问题的排查技巧
4.1 常见错误与解决办法速查表
| 报错/现象 | 可能原因 | 解决办法 |
|---|---|---|
| 表或视图不存在 | 大小写敏感,双引号强制区分大小写 | 统一标识符大小写,避免混用 |
| 语法分析出错 | 使用了达梦不支持的 MySQL 特有语法 | 查看是否涉及 LIMIT、反引号、IFNULL 等 |
| 字符串超长 | VARCHAR 长度按字节计算,LENGTH_IN_CHAR 未开启 | 初始化时开启按字符长度,或扩大 DDL 长度 |
| 插入后查不到数据 | '' 被当成 NULL,常见于非 MySQL 兼容模式 | 改写空字符串判断逻辑 |
| ON UPDATE 报错 | 达梦不支持该语法 | 应用层维护更新时间,或使用触发器 |
| GROUP_CONCAT 不存在 | MySQL 特有聚合函数 | 改为 LISTAGG,注意长度限制 |
| 存储过程报“变量未声明” | 达梦要求变量集中在 AS 后声明 | 把 DECLARE 段搬到 BEGIN 前 |
| 触发器里 NEW 报错 | 达梦要求写 :NEW | 补冒号并检查触发器类型 |
| 批量插入非常慢 | 没有关闭约束/索引,或未使用批量参数 | 导入前删索引,配置批量提交 |
这张表只是高频问题,实际项目里一定还有没列到的。我一般遇到新报错,先做三件事:看完整错误信息里的对象名、确认目标库版本、对比 MySQL 原写法。百分之八十的问题都能用这个方法快速定位。
4.2 乱码与字符集问题定位
乱码问题几乎每个迁移项目都会碰到。最典型的场景是:DTS 导完数据后,看到中文正常,但应用查询时乱码;或者应用写进去正常,DTS 导出来却是乱码。这类问题基本都是客户端字符集和数据库字符集不一致导致的。
第一步先统一达梦数据库字符集,确认是 UTF-8。第二步改 JDBC 连接字符串,在达梦驱动里显式声明字符集:
jdbc:dm://192.168.1.10:5236?characterEncoding=UTF-8第三步检查应用所在服务器和 MySQL 老环境之间的字符集。有一种情况很坑:MySQL 导出文件本身是 latin1,没做转码直接导入达梦,结果所有中文全变问号。这种问题在数据层面已经不可逆了,最好重新从源头导出并确认编码。
还有一个细节:Navicat 等图形化工具连接达梦时,连接高级设置里也有字符集选项。如果你在工具里看到乱码,但应用里正常,说明是工具连接的字符集设置问题,不是数据问题,别被带偏。
4.3 性能排查与 SQL 改写建议
迁移后性能问题通常有两个来源:一是统计信息不完整,执行计划退化;二是 SQL 本身依赖了 MySQL 特有优化路径,达不到同样效果。
第一个问题好解决。数据导完之后,立刻对全库执行统计信息更新:
DBMS_STATS.GATHER_SCHEMA_STATS('DM_USER');然后再跑业务慢日志,看哪些 SQL 执行计划有问题。达梦自带的一些视图可以查询执行计划,比如EXPLAIN语句,或者 DM 管理工具里的图形化执行计划功能,建议在压测阶段就用起来。
第二个问题需要具体分析。比如 MySQL 里用LIMIT配合索引能快速取前 N 条,达梦里如果没查到等价写法,可能就会走全表扫描。再比如GROUP_CONCAT改成LISTAGG后,如果分组字段没有索引,内存排序的开销可能比原来大。遇到这种问题,不要盲目调优参数,先把 SQL 拿出来做执行计划对比,找到具体是哪一个算子性能差,再针对性加索引或改写。
另外提醒一个容易忽视的问题:达梦对 NULL 值的索引处理和 MySQL 不一样。MySQL 允许唯一索引下多个 NULL 同时存在,达梦默认行为也类似,但如果在批量导入时开启了一些特殊约束或参数,可能导致唯一索引校验异常。这类问题在数据量大的表上尤其隐蔽,平时查询不报错,只有重复数据产生时才暴露,所以导入阶段一定要在测试环境做一次重复数据插入测试。
5. 个人体会与后续扩展建议
这次迁移项目给我的最大体会是:不要试图把达梦改造成 MySQL,也不要指望一套 SQL 能无差别兼容两个数据库。国产化迁移的价值不只是换掉底层组件,更是一次把 SQL 收敛、规范化、集中管理的机会。我在项目后期把所有 Mapper 层的关键 SQL 全部收敛到独立文件,统一封装公共方法,后续再要适配别的数据库,改动范围就小很多。
如果你后面还要做同类迁移,建议先做一次真实 SQL 抽取和静态扫描,花钱花时间都值得,它能把整个项目的改造量预估从“大概一个月”变成“明确到人天”。另外,迁移完成后别急着销毁 MySQL,至少保留一个只读副本跑一周,方便回退和对照。等双跑期结束,再清理临时脚本和旧库。
最后再分享一个小技巧:在达梦里遇到一个 MySQL 语法不兼容时,先别急着硬改成达梦原生语法,去官方文档查一下目标版本是否已经支持该函数。达梦每个小版本迭代都会补一批兼容能力,之前我们以为无解的CONCAT_WS,换了一个稍新的版本后居然可以直接用。版本差异很大,用旧版踩过的坑,在新版里可能根本不是问题。