MySQL 迁达梦踩坑实录:FIND_IN_SET、timestamp(6) 和一整批要改的函数

先摆三个报错

最近一直在做 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,而是建库前那四个参数定没定对。

我们项目上把这类兼容性问题整理成了一份清单,按函数、类型、字符集、权限分了四组,每条带现象和改法。如果你手上也有正在迁的系统,可以找我一起过一遍风险点。

相关推荐
n 55!w !1081 小时前
MySQL笔记整理2
数据库·笔记·mysql
aiks_yuan1 小时前
MySQL 批量插入很慢怎么办:rewriteBatchedStatements 实测 10 万行从 4.5 秒到 1 秒(JDBC、MyBatis、LOAD DATA 对比)
mysql
李纲明2 小时前
WordPress 提示「建立数据库连接时出错」:先核 wp-config 与数据库服务,再查表损坏与磁盘配额
数据库·wordpress·devdiy
freejackman2 小时前
【架构篇】双遍历 + 多栈协同:手撸 SQL 血缘引擎的架构思考
java·大数据·数据库·hive·sql·架构·spark
杨云龙UP2 小时前
TDengine 社区版磁盘空间不足故障处理 SOP:历史日志清理、日志保留策略及数据保留 200 天
数据库·时序数据库·tdengine·tsdb·历史日志清理·trim database·logkeepdays
企业数字化笔记2 小时前
业务单据怎样接入流程中心?启动条件、实例关联与失败回退
java·spring boot·mysql
betazhou2 小时前
MySQL Oracle官方备份工具
数据库·mysql·oracle
一万年太久。2 小时前
mysql主从复制
数据库·mysql
车载中间件通信3 小时前
[TC8测试]TCP_FLAGS_PROCESSING_06
数据库·网络协议·tcp/ip·中间件·tc8