先摆三个报错
最近一直在做 MySQL 到达梦的迁移,把踩过的坑按类型整理了一遍。先看三个最典型的,它们看起来毫无关系,其实是三件不同的事。
第一个,FIND_IN_SET 直接找不到。
sql
SELECT * FROM t_role WHERE FIND_IN_SET('admin', role_codes);
这条在 MySQL 里跑得好好的,迁到达梦后报函数不存在。
第二个,timestamp(6) 建表过不去。
sql
CREATE TABLE t_log (
id BIGINT,
create_time TIMESTAMP(6)
);
迁移时报类型不支持,或者迁过去了但精度丢了。
第三个,中文被悄悄截断。
字段定义是 varchar(50),源库存 50 个汉字,迁完发现尾部少了几个字,而且不报错、不告警。
三个报错,三种层级
把它们混在一起改,最后一定返工。分开看就清楚了:
- FIND_IN_SET 属于语法层------函数不存在,是"这个名字在我这儿没有"
- timestamp(6) 属于类型层------类型存在,但精度映射没对齐
- 中文截断属于存储层------类型和语法都对,字符长度语义不一致
达梦对 Oracle 的兼容性是行业公认最强的一档,但 MySQL 的函数集覆盖不是同一个量级。所以从 MySQL 迁过来,语法层的坑会明显比从 Oracle 迁更多。这一点在迁移评估阶段就应该预判到,而不是等报错了再一个个补。
逐个改
一、FIND_IN_SET:先看调用点有多少
方案 A,调用点少,直接改 SQL。
sql
SELECT * FROM t_role
WHERE CONCAT(',', role_codes, ',') LIKE '%,admin,%';
关键在那个 CONCAT。role_codes 里可能存的是 admin,editor 或 editor,admin,如果你直接写 LIKE '%admin%',superadmin 也会被命中。首尾各补一个逗号,把"包含"精确约束成"是其中一项",才不会误伤。
方案 B,调用点几十上百处,在数据库侧建一个同名函数。
改应用层代码风险大、回归测试成本高的时候,把改造成本压到数据库侧更划算。但要注意两件事:一是 NULL 传参和空字符串的行为必须和原库对齐,否则边界数据会错;二是自定义函数一旦上线就成了新的技术债,要写进文档。
判断线: 调用点少于 10 处改 SQL;超过 30 处建函数;中间这段看回归测试成本。
二、timestamp(6):不要依赖默认类型映射
迁移工具的类型映射默认不一定带精度。稳妥做法是迁移前先从评估报告里把所有带精度的列筛出来------(3)、(6) 这些------单独列一张映射表,逐列确认目标类型,再让工具按映射表走。
这一步花不了多少时间,但能省掉上线后"日志时间戳精度对不上、同秒数据排序错乱"这类很难查的问题。
三、中文截断:迁移评估阶段就要定
这条是三个里最危险的,因为它不报错。
它的根源是字符集语义差:源库和目标库的字符集不同,中文字符占用的字节数不同,于是同一个 varchar(50) 在两边的实际容量不一样。达梦官方文档里对这个场景的处理建议很明确------从 GB18030 这类字符集迁到 UTF-8 目标库、并且库里存有大量中文时,字符长度要按倍数扩展,按 2、4 或 8 倍设置。
如果你等到数据迁完才发现在截断,补救办法只剩下改表结构加重迁,代价比一开始定对参数高得多。所以这一条必须在评估阶段锁死。
上线前必须逐项确认的四个初始化参数
下面这四项都是建库时确定、建完之后改不了的初始化参数。这意味着它们不是"割接时再调"的东西,而是评估阶段就要拍板的。
| 参数 | 为什么必须在建库前定死 |
|---|---|
| CASE_SENSITIVE 大小写敏感 | 建库后改不了。源库大小写不敏感、目标库敏感时,所有靠大小写"碰巧能跑"的 SQL 会成批报错,而且报错点往往分散在完全不相干的业务里 |
| PAGE_SIZE 页大小 | 建库时确定,直接影响大字段存储与单行上限 |
| EXTENT_SIZE 簇大小 | 影响小表的空间碎片与后续扩容行为 |
| CHARSET 字符集 | 与上面的中文截断问题直接相关 |
我见过最难受的一种情况:系统迁完、测试通过、上线一周后,某个边角业务才报出一条大小写相关的错。查两天,最后发现是建库参数。这类问题的排查成本远高于事前确认。
迁移顺序会影响排查速度
达梦的自动化迁移工具是按固定顺序走的:序列 → 自定义类型 → 表结构 → 视图 → 表数据 → 主键 → 索引。
这个顺序不是随便排的,它的意义是规避对象之间的依赖。你手工补迁失败对象的时候,最好按同样的顺序来。很多"外键找不到父表""序列不存在"的报错,其实不是你改错了,而是你顺序反了------先建了带外键的表,父表还没建。
八成还会撞上的几类
按我这两轮的记录,下面这几类出现的频率不比上面三个低:
- GROUP_CONCAT:分隔符和结果长度限制的行为差异
- LIMIT 分页:深分页的写法与执行计划差异
- 自增列:和序列的对应关系,尤其是有业务依赖自增值的场景
- 空字符串与 NULL:两者是否等价,直接决定唯一索引和查询条件的结果
- 反引号与标识符大小写:写法兼容与对象名解析
一句总结
MySQL 迁达梦,语法层的坑最多但最好改,类型层要提前建映射表,存储层最危险因为不报错。真正决定迁移顺不顺的,往往不是改了多少 SQL,而是建库前那四个参数定没定对。
我们项目上把这类兼容性问题整理成了一份清单,按函数、类型、字符集、权限分了四组,每条带现象和改法。如果你手上也有正在迁的系统,可以找我一起过一遍风险点。