1. 迁移前奏:先搞清楚你的库到底有多“歪”
1.1 盘点家底:对象与数据量评估
我见过太多人拿到迁移任务就直接打开工具开导,结果导到一半报错,回头一看才发现源库里有几十个视图、一堆自定义函数、还有定时任务没迁移。所以第一步不是动手,而是先把源库的家底盘清楚。
你需要梳理的对象清单包括:表、视图、存储过程、自定义函数、触发器、定时事件、索引、约束、外键关系。别看这些好像都差不多,MySQL和达梦在每一个对象类型上都有不同程度的方言差异,后面我会逐个说。
盘点数据量的方法很简单,直接查 information_schema 就行:
sql复制SELECT
table_schema,
table_name,
table_rows,
ROUND((data_length + index_length) / 1024 / 1024, 2) AS size_mb
FROM information_schema.tables
WHERE table_schema = 'your_db'
ORDER BY size_mb DESC;
注意 table_rows 是估估值,InnoDB 下并不精确,但用来排序找大表足够了。更准确的做法是对核心大表执行 COUNT(*),不过生产环境别浪,挑业务低峰期做。
另外还要顺便摸一下表的“形态”:是不是分区表?有没有自增列?有没有 ON UPDATE CURRENT_TIMESTAMP 这种 MySQL 特性?字段默认值有没有用函数表达式?这些在达梦里可能完全不支持,早发现早处理,别等迁移工具帮你踩雷。
提示:把对象清单导成 Excel,标记每类对象的数量、是否使用了特殊特性,这个清单就是你整个迁移工期的排期表。
1.2 达梦与MySQL的核心差异速览
很多“迁移失败”并不是技术做不到,而是没有认识到这两个数据库在架构层面的天然差异。我把最影响迁移路径的内容列成一张速查表:
| 对比项 | MySQL | 达梦8 |
|---|---|---|
| 大小写敏感 | 与操作系统相关,通常库名表名小写 | 实例初始化参数 CASE_SENSITIVE 决定,1为敏感,0为不敏感 |
| 库与模式概念 | database 即 schema | 用户(USER)与模式(SCHEMA)关联,一个用户默认对应一个同名模式 |
| 自增列 | AUTO_INCREMENT |
IDENTITY(1,1) 或序列 + 默认值 |
| 时间类型精度 | DATETIME(6) 微秒精度 |
TIMESTAMP 精度可达微秒,DATE 在达梦中包含时分秒 |
| 分页语法 | LIMIT offset, count |
兼容模式下支持 LIMIT,原生也支持 TOP 和 FETCH |
| 字符串连接 | CONCAT() |
` |
| 空值处理 | IFNULL() |
NVL() / IFNULL() |
NULL 排序 |
默认升序 NULL 排最前 | 默认升序 NULL 排最后,可通过 NULLS FIRST/LAST 控制 |
| 存储引擎 | InnoDB/MyISAM 等 | 统一页式存储,无引擎概念 |
| 双引号 | 默认字符串或标识符(看 ANSI_QUOTES) |
默认标识符;字符串必须用单引号 |
优先级最高的一个决策点是:大小写策略必须在初始化实例前确定,一旦定了后期几乎没法改。你要先摸清现有 MySQL 里表名、列名、存储过程体里的大小写使用习惯,再决定达梦实例的 CASE_SENSITIVE 取值。
我个人的建议是:如果你只有一个迁移任务,尽量把达梦的 CASE_SENSITIVE 设置为 0(不敏感),这样 MySQL 侧那些“表名字段名随手写、大小写混用”的习惯在达梦侧不会炸。缺点是后续新开发的 SQL 如果不规范,可能查出来列名大小写和定义不一致,但总体来说迁移阶段省事太多。
1.3 迁移策略选型:全量、增量、双写?
迁移策略不是拍脑袋定的,取决于业务方给你多少停机窗口。
- 停机迁移:最省事。业务停写,导出全量数据,导入达梦,校验完成后切换应用连接。适合数据量在几百 GB 以内、业务允许停 2-4 小时的中小系统。
- 全量+增量:先全量迁移,再用日志或 CDC 工具做增量追平,切换前短暂停写。适合数据量大、停机窗口短的场景。热词里提到的
seatunnel 达梦 cdc就是这类场景下的典型玩法。 - 双写并行:新老并行写一段时间,等两边数据一致再切换。这个方案对应用改造要求高,建议只在核心交易链路使用。
对于第一次做 MySQL 到达梦迁移的团队,我最推荐“全量+短暂停写”的组合:先用全量迁移工具导数据,演练几轮把耗时长的问题暴露出来,切换当天停写 15 分钟做增量补齐,然后直接改连接串。这套方案代码改动小,出问题也容易回滚。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 迁移工具与路径:别再一张张导表了
2.1 达梦官方数据迁移工具(DTS)
达梦官网提供的数据迁移工具(DTS)是 GUI 程序,Windows 和 Linux 版本都有,通常和数据库安装包一起打包。它是大多数人迁移 MySQL 到达梦的首选工具。
DTS 的工作流程很直观:新建工程 → 新建迁移 → 配置源库(MySQL)和目标库(DM8)→ 选择迁移对象 → 保存执行。
实操时有几个细节特别值得注意:
第一,源库配置选择 MySQL 后,需要填 IP、端口、用户名密码和数据库名。如果你要迁移的 MySQL 有多个 database,DTS 会分别列出,记得勾选全选。
第二,DTS 支持“先迁移对象、后迁移数据”的模式。默认会先把建表语句转换成达梦风格并执行,然后开始灌数据。如果你在对象转换阶段看到某张表的 DDL 标红报错,可以单独编辑,不用整体停下来。
第三,DTS 里可以配置“批量提交行数”和“批量提交间隔”,默认值下小表没问题,大表建议把批量行数调到 1000 甚至 5000,能明显缩短整体导入时间。实测一个 500 万行的业务表,默认参数跑了 20 分钟,调大批量后 8 分钟跑完。
提示:DTS 在迁移触发器、视图这类对象时,默认依赖顺序可能不对(视图依赖另一张视图时)。建议先手动对比对象清单,按依赖顺序分批选择要迁移的对象。视图间嵌套三层以上时,DTS 偶尔会漏掉部分依赖,迁移完务必核对视图数量。
2.2 命令行与脚本方式
如果你没有图形化环境,或者想做自动化重跑,命令行方式是更好的选择。
最经典的组合是 mysqldump 导出 + 改造 SQL + disql 导入。具体步骤如下:
- 用
mysqldump导出--no-data的 DDL,存成 SQL 文件。 - 写一个小脚本把不兼容的语法批量替换(例如把
`反引号去掉,把ENGINE=InnoDB去掉,把AUTO_INCREMENT改为达梦的IDENTITY,把COMMENT去掉或转换成达梦的COMMENT ON语句)。 - 用达梦的
disql执行:
bash复制disql SYSDBA/SYSDBA@localhost:5236
start /path/to/convert_ddl.sql
- 数据导入用
dmfldr快速装载工具,或者直接用INSERT语句批量执行。dmfldr是大批量数据导入的首选,官方给的性能数据比逐条 INSERT 高一个数量级。
bash复制dmfldr SYSDBA/SYSDBA@localhost:5236 CONTROL=/path/to/control.ctl DATA=/path/to/data.txt
控制文件写法大致如下:
text复制LOAD DATA
INFILE '/path/to/data.txt'
INTO TABLE ORDERS
FIELDS TERMINATED BY '|'
(ORDER_ID, CUSTOMER_ID, ORDER_AMOUNT, ORDER_TIME)
这套方案的好处是全程可脚本化,出错重跑成本低。坏处是你需要自己处理大量 MySQL 方言到达梦方言的转换逻辑,对 MySQL 和达梦两边的细节都要熟。
2.3 用 ETL 工具兜底复杂逻辑
对于有大量存储过程、函数、触发器需要转换的场景,或者你所在团队本身就在用 Kettle、DataX 做数据同步,可以考虑用 ETL 工具做中间层。
- Kettle:在热词里出现了
kettle连接达梦,说明实践的人很多。Kettle 支持通过达梦 JDBC 驱动连达梦,图形化设计“表输入 → 表输出”步骤,处理字段映射和类型转换非常直观。 - DataX:阿里开源的离线同步工具,通过达梦读写插件也能工作。它的优点是基于 JSON 配置,适合批量组织和自动化调度。
- Seatunnel:热词里提到的
seatunnel 达梦 cdc,适合需要持续增量同步的场景,但配置复杂度比前两者高,适合有一定数据平台能力的团队。
选择建议:如果只是“一次性迁移”,我强烈建议优先考虑 DTS;如果系统量大、后续要长期做异构数据同步,再考虑 Kettle 或 DataX;如果要做持续增量,才考虑 Seatunnel 类 CDC 方案。
3. 字段类型与SQL兼容性:迁移过程的“重头戏”
3.1 字段类型映射表
早期很多资料鼓吹“达梦兼容 MySQL 所以类型都不用改”,实际迁移时你会发现确实大部分类型都能直接映射,但坑都在细节上。下面是我整理的常用映射参考:
| MySQL 类型 | 达梦类型 | 注意事项 |
|---|---|---|
TINYINT |
TINYINT 或 SMALLINT |
达梦 TINYINT 支持 0-255 无符号或 -128~127 有符号,按需使用 |
SMALLINT |
SMALLINT |
无特别问题 |
MEDIUMINT |
INT |
达梦没有 MEDIUMINT,用 INT 代替 |
INT / INTEGER |
INT |
注意达梦 INT 是 4 字节,范围与 MySQL 一致 |
BIGINT |
BIGINT |
无特别问题 |
DECIMAL(p,s) |
DECIMAL(p,s) |
精度和标度直接对应 |
FLOAT / DOUBLE |
FLOAT / DOUBLE |
达梦默认精度与 MySQL 有差异,业务对金额敏感请用 DECIMAL |
CHAR(n) / VARCHAR(n) |
CHAR(n) / VARCHAR(n) |
注意达梦的 LENGTH_IN_CHAR 参数会影响 VARCHAR 长度单位,实例初始化时就要定 |
TEXT |
TEXT / CLOB |
MySQL 的 TEXT 在达梦里可能映射为 TEXT 类型,但大批量查询性能一般,必要时改用 VARCHAR 大字段 |
LONGTEXT |
CLOB |
CLOB 操作有长度限制,需要分块处理 |
BLOB / LONGBLOB |
BLOB / BLOB |
达梦 BLOB 足够用,但客户端展示受限 |
DATE |
DATE |
注意达梦 DATE 包含时分秒,如果 MySQL 只存日期,迁移后查询显示可能多了 00:00:00,可用 TO_CHAR(col,'YYYY-MM-DD') 处理 |
DATETIME |
TIMESTAMP |
MySQL 的 DATETIME 与时区无关,达梦 TIMESTAMP 同样与时区无关(除非用 WITH TIME ZONE),基本对等 |
TIMESTAMP |
TIMESTAMP |
MySQL TIMESTAMP 有时区自动转换,达梦默认没有,迁移后时间值不变更安全 |
YEAR |
INT |
MySQL 的 YEAR 在达梦里没有对等类型,通常用 INT 存年份 |
ENUM |
VARCHAR + CHECK 约束 |
建议直接转为 VARCHAR,CHECK 是否保留根据业务需要 |
SET |
VARCHAR |
转成逗号分隔的字符串 |
最有争议的是 VARCHAR 长度:MySQL 里 VARCHAR(255) 表示 255 个字符,而达梦在 LENGTH_IN_CHAR=0(默认)下表示 255 个字节。对于中文场景,如果一个字段要存 255 个汉字,MySQL 侧没问题,达梦默认就存不下,报“字符串超长”。解决方案是迁移时把达梦相关表字段长度乘以字符集倍数,比如 UTF-8 下 VARCHAR(255) 改成 VARCHAR(765) 或直接用 VARCHAR(255 CHAR) 这种显式写法。
注意:
VARCHAR(n CHAR)是达梦支持的写法,更推荐在建表时固定使用。另外如果整个实例都能统一用字符语义,在初始化达梦时把LENGTH_IN_CHAR设为 1,这样所有 VARCHAR 都按字符长度解释,迁移省心很多。但这个参数也是实例级不可动态修改的,需要在安装阶段就规划好。
3.2 存储过程、函数与触发器的改写
这一块是迁移过程中最耗费精力的部分,也是最需要“人工干预”的部分。千万别指望迁移工具一键把 MySQL 的存储过程变成达梦的存储过程,工具能做的是把大框架搭出来,细节语法必须手工处理。
先看几个核心差异:
DELIMITER 处理。 MySQL 客户端用 DELIMITER // 来区分整个存储过程体,达梦没有这个概念。你用工具迁移时,它会自动剥离掉 DELIMITER 和相关语法,但如果手写转换,记得把所有 DELIMITER 行直接删除。
变量声明位置。 MySQL 允许在 BEGIN...END 块内任意位置声明局部变量(实际上通常都在开头),达梦要求遵循更严格的块结构,变量声明必须在可执行语句之前。迁移时如果原有的存储过程变量声明穿插在业务逻辑中间,需要顺手把声明全部提到最前面。
游标语法。 MySQL 的游标是 DECLARE cur CURSOR FOR SELECT ...,达梦的游标有两种写法:
sql复制-- 达梦:声明游标变量
DECLARE
CURSOR cur FOR SELECT id FROM orders;
在循环读取时,MySQL 习惯 FETCH cur INTO var;,达梦支持同样的写法,兼容性尚可。但 MySQL 里常用的 DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; 这种“NOT FOUND 处理器”语法达梦也是支持的,只是细节上不同版本有差异,迁移后一定要逐个存储过程做 EXEC 冒烟测试。
函数替换。 存储过程里最常见的函数差异列表如下:
| MySQL 写法 | 达梦推荐写法 |
|---|---|
IFNULL(a, b) |
NVL(a, b) |
DATE_FORMAT(d, '%Y-%m-%d') |
TO_CHAR(d, 'YYYY-MM-DD') |
STR_TO_DATE(s, '%Y-%m-%d') |
TO_DATE(s, 'YYYY-MM-DD') |
NOW() |
SYSDATE() 或 NOW() |
CURDATE() |
TRUNC(SYSDATE) |
CONCAT(a, b) |
`a |
GROUP_CONCAT(col) |
LISTAGG(col, ',')(注意达梦也有 WM_CONCAT 但 LISTAGG 更标准) |
IF(cond, a, b) |
CASE WHEN cond THEN a ELSE b END |
触发器方面,MySQL 的 FOR EACH ROW 语法达梦同样支持,但新旧行引用方式不同:MySQL 用 NEW.col / OLD.col,达梦也支持这种写法(兼容模式下),但有些版本更推荐 :NEW.col / :OLD.col,迁移后检查一下没有语法错误就行。
3.3 特殊对象与特性处理
除了表和数据,DTS 通常也能迁移视图、序列、索引、约束,但有几个 MySQL 特性需要格外留意:
分区表。 MySQL 分区表是创建表时用 PARTITION BY 子句,达梦的分区表语法是 PARTITION BY RANGE(...) (PARTITION ... VALUES LESS THAN (...)),大体思路一致,但细节和类型支持上有差异。建议在迁移工具转换后人工核对分区边界值,特别是日期分区和 MAXVALUE 的写法。
定时事件。 MySQL 的 EVENT(定时任务)达梦没有对等对象,需要用达梦的“作业”(Job)来实现。达梦的作业系统在管理工具里有可视化界面,也可以在 disql 里用 SP_CREATE_JOB 系列系统过程创建。迁移时把每个 MySQL Event 的调度逻辑翻译成达梦作业,主要关注执行频率、首次执行时间、执行存储过程名称。
约束与索引命名冲突。 MySQL 的索引名在一个库内只需唯一,达梦的模式内约束、索引名也要求唯一,但如果从多个 MySQL 库合并到一个达梦模式,很可能出现名称冲突。轻则迁移报错,重则自动改名导致应用 SQL 中按名称访问索引的逻辑失效。迁移后务必用如下 SQL 检查是否有重名对象:
sql复制SELECT table_name, index_name, COUNT(*)
FROM all_indexes
WHERE owner = 'YOUR_SCHEMA'
GROUP BY table_name, index_name
HAVING COUNT(*) > 1;
4. 实操过程:一个订单系统的完整迁移
4.1 项目概况与目标架构
假设我们有一个订单管理系统,源库 MySQL 5.7,一共 68 张表、14 张视图、22 个存储过程、3 个触发器、2 个定时事件,总数据量约 320GB,其中最大的订单流水表约 2 亿行。目标库达梦 8(DM8),部署在麒麟 V10 操作系统上,架构是单实例。这个案例非常有代表性,很多企业的“订单库迁移”就是这种规模。
迁移前我们先把达梦实例初始化,注意以下参数:
CASE_SENSITIVE设为 0,避开大小写问题;LENGTH_IN_CHAR设为 1,VARCHAR 按字符长度存储;PAGE_SIZE设为 32(单位是 KB),大表顺序扫描性能更好;CHARSET设为 UTF-8。
这几个参数在初始化实例时一次性确定,后续不能动态修改。很多项目就是因为没想清楚就初始化完,后面发现中文长度不够、大小写问题一堆,只能推倒重来。
4.2 执行迁移的完整步骤
第一步:使用 DTS 创建全对象迁移任务。
我们选择 MySQL 到 DM8 的迁移,源库填 MySQL 连接信息,目标库填达梦信息。对象选择界面里,我把所有表、视图、存储过程、函数、触发器全部勾上。首次执行时 DTS 报了几个错误:三张表的建表语句不兼容、两个存储过程有语法转换错误。我直接在 DTS 的“纠正脚本”弹窗里把达梦 DDL 改掉,重新执行。
第二步:数据灌入与分批提交参数调优。
表结构建好后,DTS 开始批量灌数据。我把每批提交行数调到 2000,关闭“使用批量绑定前检查”选项。对小表用默认并发,对订单流水这种超大表单独创建一个迁移任务,并打开 DTS 的“并行加载”选项,并发度设为 4。实测效果:2 亿行订单流水表,并行加载用时 43 分钟,全库 320GB 数据整体约 4 小时完成。
提示:DTS 在数据一致性上还是比较可靠的,但大表传输时如果网络抖动,偶尔会出现“数据读取失败”的报错。遇到这种情况不用慌,记录当前已完成行数,重新创建一个从该表开始的增量任务即可,源库数据没变化时重复跑不会产生脏数据。
第三步:存储过程手工改写。
DTS 生成的 22 个存储过程有 9 个直接通过,8 个小改后通过,5 个完全需要重写。我再三强调:存储过程迁移后必须做冒烟测试。我们的做法是写了一套测试脚本,逐个 EXEC 存储过程,用典型入参验证返回结构、影响行数和错误码是否与 MySQL 一致。有个存储过程在 MySQL 里依赖了一个自定义函数,DTS 没有把函数和过程按依赖排序执行,导致过程创建失败。解决方法是先手动创建依赖的函数,再执行过程创建脚本。
第四步:数据校验。
迁移完成后的校验分四层:
- 对象数量校验:表、视图、过程、函数、触发器数量与源库一一对应;
- 行数校验:每张表执行
COUNT(*),和 MySQL 对比; - 关键指标校验:对核心表抽样做
SUM(金额)、MAX(时间)等聚合对比; - 应用冒烟验证:把测试环境的应用连接串切到达梦,跑一遍核心业务流程。
4.3 应用侧切换要点
数据迁移成功只能算完成一半,另一半是应用适配。
首先,JDBC 驱动要更换。MySQL 用 com.mysql.cj.jdbc.Driver,达梦用 dm.jdbc.driver.DmDriver,连接串也从 jdbc:mysql://ip:3306/dbname 换成 jdbc:dm://ip:5236。注意达梦 JDBC 连接串同样可以指定 schema:
java复制jdbc:dm://192.168.1.100:5236?schema=ORDERS
其次,如果应用使用 MyBatis,分页插件通常靠 MySQL 的 LIMIT 方言生成 SQL,换成达梦后要么使用支持达梦方言的分页插件,要么在 SQL 里改为达梦兼容的分页写法。达梦在兼容模式下支持 LIMIT n OFFSET m,所以很多应用其实不需要改代码。
再说连接池。Druid、HikariCP 都支持达梦,但建议把连接池的 validationQuery 改成 SELECT 1 FROM DUAL 或 SELECT 1,确保连接探活正常。另外达梦的连接数默认比较保守,初始化后 MAX_SESSIONS 默认 100,如果应用并发高,记得调大。
最后是时间处理。MySQL 的 DATETIME 和达梦的 TIMESTAMP 精度保持一致时基本无感。但有一些遗留 SQL 用了 DATE_FORMAT(now(), '%Y-%m-%d %H:%i:%s'),在达梦里没有同名函数,需要全局搜出并改成 TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS')。
5. 常见问题与排查技巧实录
5.1 迁移中的高频报错及定位思路
这里整理一份速查表,是我在多个迁移项目里反复踩过的坑:
| 报错场景 | 典型报错 | 原因与处理 |
|---|---|---|
| 大小写敏感问题 | 无效的表名或视图名 |
初始化时 CASE_SENSITIVE=1,而代码里用了大小写混合的标识符。统一改小写或初始化时使用不敏感模式 |
| 字符串超长 | 数据超长, 字符串截断 |
VARCHAR 长度按字节计算,中文占多字节。在初始化时设 LENGTH_IN_CHAR=1,或把字段长度改为 VARCHAR(n CHAR) |
| 自增列冲突 | 违反表唯一约束 |
迁移后自增列的当前值没有同步源库的 AUTO_INCREMENT 值,插入新数据时撞主键。手工把 IDENTITY 起点改到源库最大值 |
| 函数不存在 | 没有此函数 |
MySQL 自定义函数在达梦没迁移成功或函数名大小写不一致。检查 DTS 的转换日志,手工创建 |
| 存储过程编译失败 | 编译错误, 对象 无效 |
存储过程依赖的自定义函数或表没有按依赖顺序创建。梳理对象依赖图,按顺序执行 |
| 死锁或阻塞 | 检测到死锁 |
达梦默认行锁机制与 MySQL 有差异,并发事务冲突时报错。调整应用事务顺序,或把初始化参数 DEADLOCK_CHECK_TIME 适当调大 |
| 时间字段错乱 | 时间少了 8 小时或多了 8 小时 | JDBC 连接串时区设置问题。达梦 JDBC 在连接串加 &useJDBCCompliantTimezoneShift=true,或检查应用 JVM 默认时区 |
| 浮点数精度不准 | 金额对不上 | MySQL 的 FLOAT/DOUBLE 在达梦转换后有精度差异。建议迁移后把关键金额字段改为 DECIMAL,并重刷数据 |
自增列这个坑我多说一句:MySQL 表如果有 100 条数据,AUTO_INCREMENT 已经是 101,DTS 迁移后表的 IDENTITY 起点通常从 1 开始,如果不改,应用插入新数据时就会收到主键冲突。处理方法是迁移完成后执行:
sql复制ALTER TABLE ORDERS ALTER COLUMN ORDER_ID RESTART WITH 1000000;
这里的 1000000 改成源库 AUTO_INCREMENT 的下一个值即可。
5.2 数据一致性校验的“笨办法”与“巧办法”
校验数据一致性,最笨但最可靠的方法就是行数对比。但我发现只有行数对比远远不够——两张表可能行数一样,但某一行数据内容不同甚至错位。所以我建议做三层校验:
第一层:行数 + 聚合值。每张表统计 COUNT(*),对关键金额、数量字段做 SUM 和 AVG。这层能快速暴露缺数、漏导、重复导的问题。
第二层:抽样明细对比。对每张表按主键抽样 1% 的记录,把主键和关键字段拼成字符串,两边算出哈希,对比哈希集合是否一致。自己写脚本也行,用 ETL 工具也有现成的“数据比对”步骤。
第三层:应用层验证。这是最接近线上效果的校验方式,也是很多团队忽略的。切换测试环境后,让测试同事按照核心用例回归一遍业务,重点看新增、修改、删除操作是否正常,因为数据迁移只保证存量正确,新增数据写入达梦后的行为需要靠业务来验证。
5.3 迁移后的性能问题排查
数据迁过去了、功能也能用了,这时候最容易出现的问题是“跑得比原来慢”。有几个高频原因:统计信息缺失、执行计划没更新、索引没建全。
达梦的优化器需要统计信息来生成执行计划,数据导入后必须先收集统计信息:
sql复制DBMS_STATS.GATHER_SCHEMA_STATS('ORDERS', 100);
一次性跑完后,再把核心表的统计信息单独刷新一遍。注意达梦的 DBMS_STATS 包和 Oracle 类似,熟悉 Oracle 的同事上手很快。
另外,MySQL 侧原来依赖的隐式类型转换、函数索引、前缀索引等在达梦里不一定生效。比如 MySQL 可以对 VARCHAR 列建前缀索引 INDEX (name(10)),达梦不直接支持,需要改成函数索引 CREATE INDEX idx_name ON t(SUBSTR(name,1,10))。这类索引如果漏迁移,会导致查询计划全表扫描。
执行计划查看方式也很直观:
sql复制EXPLAIN SELECT ...;
达梦 8 的 EXPLAIN 输出比 MySQL 更接近 Oracle,重点关注 CBO 估算行数与实际行数的偏差,偏差大了大概率是统计信息没更新或者字段直方图缺失。
大表查询慢还有一个隐蔽原因:达梦的 UNDO 和 REDO 参数在迁移后没调优。默认配置下,高频更新场景可能会出现大量锁等待和回滚段不足的问题,表现为应用侧偶发“快照太旧”。建议在迁移后按实际业务的更新密度把 UNDO_RETENTION 调大,并适当增大 BUFFER_POOL_SIZE。
最后分享一个我个人的体会:数据库迁移这件事,工具解决的是 60% 的体力活,剩下 40% 全靠对业务的理解和对两个数据库语法差异的敏感度。别指望任何工具能“一键完成”,真正拉开迁移质量差距的,是前期对象排查够不够细、迁移后校验够不够严、应用适配有没有留下隐藏雷。踩过几次坑你就会发现,把“迁移完成”定义为“业务恢复且运行一周无重大问题”是最务实的标准。
