国产化迁移这件事,这两年越来越常态化。我最近刚完整走完一套MySQL 5.7迁移到达梦DM8的项目,从表结构转换、数据搬运、存储过程改造,到应用端JDBC驱动切换和上线后的性能收敛,整个过程跨度两周左右。这篇文章想把这次迁移的完整思路、具体操作和踩过的坑整理出来,给正准备做类似迁移的读者一个可以直接参考的路径。
需要说明的是,达梦数据库虽然是国产数据库,但它的语法体系与Oracle高度接近,与MySQL在细节上有不少差异。虽然DM8已经提供了MySQL兼容模式,但并不意味着可以把MySQL的建表语句和存储过程原样搬过去。迁移的核心工作不只是搬运数据,而是对整套SQL方言做一次系统性的适配。
适合看这篇文章的读者:负责国产化改造的DBA、后端开发、运维人员,以及正在评估达梦能否平滑承接MySQL业务的架构师。文章里的操作步骤和报错案例都来自实际项目,可以直接参考。
1. 迁移前的评估:先把家底盘清楚
1.1 达梦的兼容层次说明
达梦DM8其实提供了好几种兼容模式,在初始化实例或者建库的时候可以指定。最核心的参数是COMPATIBLE_MODE:0(默认,倾向于Oracle语法)、1(Oracle兼容)、2(MySQL兼容)、3(PostgreSQL兼容)。这个参数在创建实例时设定,后期改起来比较麻烦,建议一开始就按目标定好。
我的经验是:如果你的业务SQL以MySQL为主,且希望尽量少改SQL,那就把兼容模式设成2;如果团队本身对Oracle更熟,或者后续可能还要接Oracle迁移过来的系统,那就用1,让达梦按照Oracle的习惯去解析SQL。如果拿不准,我建议直接设成1走Oracle路线,因为达梦对Oracle语法的支持是最成熟的,很多MySQL函数在Oracle兼容模式下也能通过改写解决。
但要注意一点,兼容模式解决的是语法解析层面的问题,不等于所有MySQL函数都会自动映射。比如MySQL的IFNULL在MySQL兼容模式下能用,但GROUP_CONCAT这种聚合函数,达梦不管在哪个模式下都没有直接对应物,需要用LISTAGG去改。这些点后面会详细说。
1.2 迁移前要摸清的清单
动手之前,我强烈建议先对源库做一次完整的资产盘点。别偷懒,这一步能帮你提前发现80%的坑。需要梳理的内容包括:
- 实例和库:MySQL实例下有哪些业务库,哪些是核心库,哪些是历史归档库,其实很多已经不用的表没必要迁过去。把范围缩到最小,能省下大量工作量。
- 对象清单:表、视图、存储过程、函数、触发器、事件(Event)的数量。注意MySQL的Event在达梦里没有对应的调度对象,需要改造成达梦的作业(Job)或者交给业务侧定时任务处理。
- 数据量评估:总行数、总大小、单表最大行数、年增长率。这个直接影响迁移方案的选择,比如全量一次性迁移还是分批次增量。
- 字符集:MySQL这边如果是utf8mb4,达梦那边就需要建UTF-8字符集的库,否则特殊字符(emoji、生僻字)会写入失败。
- 使用了哪些MySQL特有的功能:比如分区表、全文索引、JSON类型、WITH ROLLUP、窗口函数等。窗口函数DM8.1以上已经支持,但JSON类型和全文索引就要另外想办法。
我把这些信息整理成一张表格,每个表一行,记录表名、行数、数据大小、字符集、存储引擎、是否分区、有哪些特殊类型字段。这张表在后面迁移验证阶段会反复用到。实际做下来,这个评估表大概花了我半天时间,但从结果看,这笔时间花得很值。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备:达梦怎么装、工具怎么选
2.1 达梦DM8的安装与初始化
达梦的安装包里包含了服务器端和客户端工具。Linux环境下的安装比较简单,解压后运行DMInstall.bin,会进入图形化安装界面;如果没有图形界面,也可以用命令行静默安装。安装完成后,还需要用dminit工具初始化实例。
关键配置在初始化阶段就要确认好:
bash复制./dminit PATH=/dm/data DM_INI=/dm/data/dm.ini CASE_SENSITIVE=0 CHARSET=1 COMPATIBLE_MODE=2 PAGE_SIZE=16
这里几个参数的意思分别是:
- CASE_SENSITIVE:是否区分大小写。建议设成0,不区分。达梦默认是1区分大小写,这跟MySQL默认不区分大小写的行为不一致。如果保持默认,建表时写了"User"这种双引号大小写敏感的标识符,后面查询很容易报表或视图不存在。
- CHARSET:字符集,1表示UTF-8。前面提到源库是utf8mb4,这里必须对应UTF-8,否则中文字符和特殊符号会有编码问题。
- COMPATIBLE_MODE:按前面说的设成2,走MySQL兼容。
- PAGE_SIZE:页大小,数据量大的库建议16KB,因为页大小直接决定单行记录的最大长度,后面改这个参数要重新初始化实例,非常麻烦。所以宁可在初始化时选大一点,也别等迁移完了再改。
装好实例后,还需要创建用户和表空间。达梦的用户模式是用户即Schema,一个用户对应一个Schema。比如创建一个名为APP的用户,对应的Schema就是APP,所有表都建在这个Schema下。
启动数据库服务的命令一般是:
bash复制systemctl start DmServiceDMSERVER
注意服务名格式是DmService+实例名,实例名在初始化时指定,默认是DMSERVER。这里多提一句,如果你在麒麟V10这类国产操作系统上安装,流程和Linux版本一致,但要注意检查系统依赖库,缺少libncurses之类的库会导致安装界面起不来。
2.2 迁移工具链:DTS、DBeaver、disql、dexp/dimp
达梦自带的可视化迁移工具叫DTS(数据迁移工具,在安装目录的tool目录下),这是整个迁移过程中最核心的工具。DTS支持从MySQL、Oracle、SQLServer、PostgreSQL等主流数据库迁移到达梦,也支持达梦到达梦,甚至达梦到其他数据库。
DTS能做的迁移包括表结构、数据、视图、存储过程、函数、序列等。实际用下来,表结构和数据的迁移很稳,但存储过程和视图的语法转换比较弱,很多要手动改。所以我的建议是:用DTS做表结构和数据,用人工加脚本做存储过程和视图改造。
除了DTS,日常查询和调试还需要几个工具:
- DM管理工具(manager):达梦自己的图形化管理界面,类似MySQL Workbench,可以看表结构、执行SQL、管理用户权限。
- DBeaver:我自己更喜欢用DBeaver连接达梦,前提是下载达梦的JDBC驱动。达梦的驱动类名是dm.jdbc.driver.DmDriver,URL格式是jdbc:dm://192.168.1.10:5236。把安装目录下drivers/jdbc里的DmJdbcDriver18.jar加到DBeaver的驱动管理里就能连上。
- disql:命令行工具,适合批量执行脚本,比如执行一个包含大量建表语句或者改写后存储过程的SQL文件。
- dexp/dimp:达梦的逻辑导出导入工具,类似MySQL的mysqldump。手动备份或者小规模数据迁移的时候可以直接用。
工具链选型的核心思路是:可视化迁移用DTS,批量执行和脚本化操作用disql,日常查询看需求二选一(manager或者DBeaver)。很多人问Kettle能不能连达梦,答案是可以,但需要把达梦JDBC驱动放到Kettle的lib目录里,然后在数据库连接里选择Generic Database,填上驱动类和URL。DataX同理,需要自己写一个达梦的writer插件,没有现成的。
3. 表结构与数据类型的映射:迁移的硬骨头
3.1 常用MySQL类型到达梦的映射
这一节是整个迁移里最容易出问题的地方。类型映射如果没做好,轻则字段长度变化,重则数据截断、写入失败。我整理了一张在实际项目中验证过的映射表:
| MySQL类型 | 达梦类型 | 注意事项 |
|---|---|---|
| TINYINT | TINYINT | 兼容 |
| SMALLINT | SMALLINT | 兼容 |
| MEDIUMINT | INT | 达梦没有MEDIUMINT,直接映射INT |
| INT/INTEGER | INT | 兼容 |
| BIGINT | BIGINT | 兼容 |
| DECIMAL(p,s) | DECIMAL(p,s) | 兼容,注意精度别丢 |
| FLOAT/DOUBLE | FLOAT/DOUBLE | 兼容 |
| CHAR(n) | CHAR(n) | 兼容,达梦CHAR默认是定长 |
| VARCHAR(n) | VARCHAR(n) | 注意达梦VARCHAR最大长度以字节计,和MySQL按字符计不同,utf8下要留足空间 |
| TEXT | TEXT/CLOB | 达梦有TEXT,但TEXT类型的功能受限,长文本建议直接映射CLOB |
| LONGTEXT | CLOB | 必须用CLOB |
| BLOB/LONGBLOB | BLOB | 兼容 |
| DATE | DATE | 兼容 |
| DATETIME | DATETIME | 兼容 |
| TIMESTAMP | TIMESTAMP(6) | MySQL的TIMESTAMP精度默认到秒,达梦建议用TIMESTAMP(6)保留微秒 |
| TIME | TIME | 兼容 |
| YEAR | INT | 达梦没有YEAR类型 |
| BIT(n) | BIT(n) | 兼容 |
| ENUM('a','b') | VARCHAR(n)+CHECK约束 | 达梦没有ENUM,DTS一般不会自动处理,需要手动改 |
| SET('a','b') | VARCHAR(n) | 达梦没有SET,建议VARCHAR存储,应用层解析 |
| JSON | CLOB | DM8.1之前没有JSON,8.1支持了JSON类型但和MySQL的JSON函数不通用,稳妥做法还是CLOB |
这个表格里的映射不是绝对的,实际以DTS迁出来的结果为准。我遇到过DTS把MySQL的DATETIME迁成了达梦的TIMESTAMP、VARCHAR长度偏移等小问题,所以迁完之后一定要逐表核对结构,别完全信任工具。
3.2 隐含的大小写和空串问题
除了类型映射,还有两个隐含问题很坑。
第一个是大小写。前面环境准备里提到CASE_SENSITIVE设成了0。如果按默认的1(区分大小写)来建库,那么建表语句里不带双引号的标识符会自动转成大写。MySQL里写select * from user,到了达梦变成select * from "USER",如果你的表名是小写user,就会报错。把库建成不区分大小写模式,能省掉大量改SQL的工作。
第二个是空字符串和NULL的处理。MySQL里''和NULL是两种不同的值,但很多业务其实不区分;达梦在这方面行为类似,但有些函数在传入空串时的表现和MySQL不完全一致,典型的是CONCAT、IFNULL这类。迁移后建议对涉及字符串拼接的查询做一轮针对性验证,尤其是报表SQL,我就在一个日报表上遇到过空串拼接后结果和原来对不上的问题。
3.3 自增列必须改成IDENTITY
MySQL里最常用的自增字段是AUTO_INCREMENT,达梦不支持这个关键字,对应的是IDENTITY自增列。建表时的写法是:
sql复制CREATE TABLE t_user (
id INT IDENTITY(1,1) PRIMARY KEY,
name VARCHAR(100)
);
这里的IDENTITY(1,1)表示从1开始、每次递增1,相当于MySQL的AUTO_INCREMENT初始值1、步长1。
DTS在迁移表结构时,一般会把AUTO_INCREMENT自动转换为IDENTITY,但有时候会因为主键或者索引的定义顺序,转换失败或丢掉了自增属性。所以核对表结构时,重点检查两类字段:一类是自增主键,另一类是唯一索引、联合主键。达梦对索引命名有自己的一套规则,如果DTS生成的名字和MySQL不一样,应用端如果硬编码了索引名,也要跟着改。这个细节容易忽略,等到应用报错时才反应过来。
4. 实操迁移流程:从DTS到手工导入导出
4.1 用DTS完成表和数据的迁移
DTS的用法不复杂,但有几个关键点决定了迁移质量。我这里按实际操作顺序说一遍。
第一步,打开DTS,新建迁移,选择源库类型为MySQL,填上MySQL的连接信息;目标库选择DM,填达梦的连接信息。如果你的MySQL部署在远程,注意DTS所在机器要能同时访问两个数据库。这一步如果机器之间网络隔离,就得先打通网络或者把DTS放到能同时访问两边的跳板机上。
第二步,选择要迁移的对象。这里建议勾选表、视图、存储过程、函数,但触发器可以先不选。触发器迁移出来往往带语法错误,单独留到后面处理。
第三步,设置映射关系。DTS会在左边显示MySQL的对象,右边显示对应的达梦对象,你可以手动调整每个表的映射。如果源表很多,建议先全部保持默认映射,迁完后再用脚本批量改,而不是在DTS界面上一个个点。
第四步,执行迁移。迁移过程会先建表再导数据。导数据时可以在DTS的高级选项中设置批量提交行数,默认可能是100,如果表很大,改成500或者1000能明显提升导入速度。不过批量值不是越大越好,太大可能占用太多内存,建议根据服务器的配置来,我先试的1000,后来发现内存吃紧,回退到500反而更稳定。
数据量几百G的情况下,还会遇到一个常见问题:迁移到一半网络闪断或工具崩了。DTS支持断点继续,但需要提前勾选相关选项,否则从头再来非常痛苦。我建议大表单独迁、小表批量迁,这样即使中途失败,重试的代价也可控。
4.2 用dexp/dimp做备选方案
如果你手里没有图形化环境,或者DTS在特定场景下表现不稳定,dexp/dimp是一套不错的命令行替代方案。
MySQL到达梦之间没有直接的dexp通道,实际的做法是:先从MySQL用mysqldump导出SQL,再用脚本转成达梦能识别的语法,最后用disql执行。这个过程比DTS原始得多,但对一些复杂的DDL反而更可控。我通常在以下两种场景用这套方案:
- 只需要迁几张表,不想起DTS的可视化界面。
- 表结构里有大量MySQL特殊类型(JSON、ENUM),DTS自动转换后要手动改,不如直接在导出脚本里改。
步骤大致如下:
- mysqldump导出表结构和数据,生成SQL文件。
- 用sed或Perl脚本批量替换AUTO_INCREMENT为IDENTITY、反引号去掉、ENGINE=InnoDB去掉等。
- 用disql登录达梦,执行改好的建表脚本。
- 数据部分如果量小,直接INSERT语句执行;量大就用LOAD或分批导入。
这套流程对脚本能力要求高一些,但胜在灵活。比如MySQL的CREATE TABLE语句里的ENGINE=InnoDB DEFAULT CHARSET=utf8mb4,到了达梦必须去掉,用sed一行就能替换。
bash复制sed -i 's/ENGINE=InnoDB DEFAULT CHARSET=utf8mb4//g' table_ddl.sql
替换完成后,再用disql执行:
bash复制disql APP/APP@localhost:5236
SQL> START /tmp/table_ddl.sql;
注意disql的START命令后面要跟文件的绝对路径,不然容易找不到文件。执行过程中如果某个表建表失败,disql默认会把错误打出来并继续执行下一条,不会中断整个脚本,所以跑完之后要去翻日志,确认究竟哪些表失败了。
4.3 迁移完成后的结构核对与数据校验
迁移完成不等于迁移成功,必须做校验。我见过太多项目,迁完数据看着对,上线后才发现某个表漏了外键约束、某个字段长度变了、某个自增列没生效。
结构校验的做法:在MySQL和达梦两端分别查询所有表的字段信息,生成两个文件,用diff对比。MySQL可以查information_schema.columns,达梦可以查USER_TAB_COLUMNS或ALL_TAB_COLUMNS。写个简单的Python脚本遍历所有表,对比字段名、类型、长度、是否自增、是否主键,一次就能找出所有不一致的地方。
数据校验的做法:对比行数是最基础的。对每张表,分别在两端执行count(*),比对总数。行数一致后,再抽样校验关键表的内容,找一个唯一主键,把MySQL和达梦的数据各导出一份,用diff对比。如果有几十张表,写脚本批量做,别用肉眼挑。我实际写过一个脚本,先跑完所有表的count比对,再对指定核心表做全字段哈希校验,效率比逐条SELECT高不少。
这里有一个经验:校验脚本是迁移项目里最值得投入时间的自动化工具。第一次迁移花了两小时,第二次重跑只用十分钟。后面还有增量同步、回切演练,都得靠这套脚本。所以花半天时间把校验自动化做扎实,后面省的是几天的返工时间。
5. 存储过程、视图与函数的SQL改造实录
5.1 存储过程的差异与改造方法
迁移存储过程是纯体力活,也是最容易出bug的环节。MySQL的存储过程用的是DELIMITER + BEGIN...END结构,达梦用的是Oracle风格的PL/SQL,两者在变量声明、游标、异常处理上都不一样。
我先说一个常见例子。MySQL里写一个简单的插入存储过程:
sql复制DELIMITER $$
CREATE PROCEDURE insert_user(IN p_name VARCHAR(100), IN p_age INT)
BEGIN
INSERT INTO t_user(name, age) VALUES(p_name, p_age);
END$$
DELIMITER ;
到达梦里要改成:
sql复制CREATE OR REPLACE PROCEDURE insert_user(
p_name IN VARCHAR(100),
p_age IN INT
)
AS
BEGIN
INSERT INTO t_user(name, age) VALUES(p_name, p_age);
END;
差异点主要在:
- DELIMITER不需要,达梦用分号直接结束。
- 参数声明用p_name IN VARCHAR(100)这种格式,IN/OUT关键字放在参数名后面。
- BEGIN前面要加AS或者IS。
- 参数的数量和类型顺序要一致。
这只是最基础的情况。实际项目里的存储过程往往包含循环、游标、动态SQL、事务控制。MySQL里用DECLARE声明变量,达梦里在AS和BEGIN之间直接声明变量;MySQL的PREPARE+EXECUTE动态SQL在达梦里可以用EXECUTE IMMEDIATE。
我给团队内部整理过一个MySQL存储过程到达梦改写对照表,这里分享几个高频改写:
| MySQL写法 | 达梦写法 | 备注 |
|---|---|---|
| DECLARE v_name VARCHAR(100); | v_name VARCHAR(100); | 变量声明移到AS和BEGIN之间 |
| SET v_name = 'abc'; | v_name := 'abc'; | 赋值符号 |
| IF a = b THEN ... END IF; | 同样支持 | 语法基本一致 |
| WHILE ... DO ... END WHILE; | WHILE ... LOOP ... END LOOP; | 循环结构不同 |
| FOR i IN 1..10 DO ... END FOR; | FOR i IN 1..10 LOOP ... END LOOP; | 类似 |
| LEAVE/ITERATE | EXIT/CONTINUE | 关键字不同 |
| SELECT ... INTO var | SELECT ... INTO var | 一致 |
| INSERT ... ON DUPLICATE KEY UPDATE | 不支持,需要MERGE改写 | 高频坑 |
| REPLACE INTO ... | 不支持,改MERGE | 高频坑 |
INSERT ... ON DUPLICATE KEY UPDATE和REPLACE INTO这两个是MySQL特有的,达梦都不支持,而业务里又特别常用。如果只是简单场景,可以用MERGE INTO改写:
sql复制MERGE INTO t_user t
USING (SELECT p_id AS id, p_name AS name, p_age AS age FROM DUAL) s
ON (t.id = s.id)
WHEN MATCHED THEN UPDATE SET t.name = s.name, t.age = s.age
WHEN NOT MATCHED THEN INSERT (id, name, age) VALUES (s.id, s.name, s.age);
这个改写逻辑上是等价的,但要注意MERGE在并发场景下的锁行为和MySQL的INSERT ... ON DUPLICATE KEY UPDATE不完全一样,如果业务对并发要求极高,建议应用层做分布式锁或者接受小概率冲突报错。
5.2 高频函数替换对照
函数层面的差异同样不能忽视。MySQL的函数体系与Oracle风格差异比较大,达梦虽然做了兼容,但覆盖面有限。我实际项目中碰到的替换场景如下:
- IFNULL(a, b):达梦在MySQL兼容模式下可用,但为了统一,建议改成NVL(a, b)。
- GROUP_CONCAT:达梦没有,改LISTAGG。MySQL的GROUP_CONCAT(expr ORDER BY col SEPARATOR ','),达梦的LISTAGG(expr, ',') WITHIN GROUP (ORDER BY col)。语义不完全一样,但大多数场景等价。这里要提醒一下,LISTAGG在达梦里如果拼接结果超过4000字符会报溢出,MySQL的GROUP_CONCAT默认最大长度是1024,但不同版本行为不同,迁移后要关注长字符串拼接的场景。
- DATE_FORMAT:达梦用TO_CHAR。比如DATE_FORMAT(now(), '%Y-%m-%d')改成TO_CHAR(SYSDATE, 'YYYY-MM-DD')。格式化的占位符体系完全不同,这个替换最容易出错,建议逐个核对。
- NOW()和SYSDATE:达梦兼容模式下NOW可用,但统一改SYSDATE更保险。
- CONCAT:两边都支持,但达梦拼接多个参数时如果传数字,可能会有隐式转换问题。MySQL里CONCAT(1, 'a')返回'1a',达梦里如果类型没对齐,可能报参数类型错误,需要显式CAST。
- SUBSTRING_INDEX:达梦没有,改REGEXP_SUBSTR或者拆开用SUBSTR+INSTR。
- FIND_IN_SET:达梦没有,逻辑改写。
- UUID():达梦有SYS_GUID(),但返回格式是32位十六进制字符串,不带短横线,MySQL的UUID()带短横线,如果需要一致,应用层还得处理一下。
视图的迁移类似,DTS自动迁视图时,遇到函数不兼容的就会报错。我的处理方式是:先用DTS迁视图,把失败的视图挑出来,根据上面的函数替换规则手动改写,再在达梦里执行CREATE VIEW。
5.3 触发器与事件迁移
触发器在达梦里语法和Oracle一致,MySQL和Oracle的触发器差异也比较大。开篇我说了先不迁触发器,到了这一阶段再统一处理。MySQL里每行级触发器的写法:
sql复制CREATE TRIGGER trg_user BEFORE INSERT ON t_user FOR EACH ROW
BEGIN
SET NEW.create_time = NOW();
END;
达梦的写法接近Oracle:
sql复制CREATE OR REPLACE TRIGGER trg_user
BEFORE INSERT ON t_user
FOR EACH ROW
BEGIN
:NEW.create_time := SYSDATE;
END;
注意达梦里NEW的写法是:NEW,带冒号;MySQL里是NEW,不带冒号。这个坑非常隐蔽,语法上不太容易发现,但实际编译时就过不去,报错信息是类似“无效的列名”这样,排查起来很费时间。
MySQL的Event定时任务在达梦里没有直接对应物。如果业务里有用到Event做定时清理、定时汇总,迁移后需要改成达梦的Job(系统包DBMS_SCHEDULER)或程序里的定时任务。我通常建议业务侧用现有调度平台兜底,比在数据库里维护定时任务更符合团队运维习惯,也更容易监控和告警。
6. 常见问题与排查实录
6.1 高频报错速查表
迁移和验证阶段,我整理了一份高频报错清单,这里列几个典型问题:
| 报错信息 | 可能原因 | 解决办法 |
|---|---|---|
| 表或视图不存在 | 大小写模式或Schema不对 | 检查CASE_SENSITIVE设置,确认查询时表名前带Schema,或当前连接的默认Schema是否正确 |
| 无效的关系名 | SQL里引用了不存在的对象 | 核对对象名大小写,达梦不区分大小写时用小写写也能查到大写对象,但双引号内的会严格匹配 |
| 列名无效 | 字段名大小写问题 | 同上,建表后先用desc查看实际字段名 |
| 字符串截断 | VARCHAR长度不够 | 达梦VARCHAR以字节计,utf8下一个中文占3字节,VARCHAR(255)只能存85个中文字符,需要手动扩大 |
| 数据类型不匹配 | DTS映射不当 | 检查映射后的字段类型,尤其是TEXT、JSON、ENUM |
| 不能将NULL值插入列 | 默认值没迁过来 | 核对DEFAULT约束,达梦对DEFAULT的语法解析可能与MySQL不同 |
| 无效的DEFAULT值 | 达梦不支持CURRENT_TIMESTAMP这种默认值写法 | 改为DEFAULT SYSDATE |
| 触发器编译失败 | :NEW冒号问题 | 检查NEW/OLD是否带冒号 |
| SQL命令未正确结束 | LIMIT语法不兼容 | 在非MySQL兼容模式下LIMIT不可用,改用行号ROWNUM或确认兼容模式 |
排查整张表的报错时,建议先在disql里执行一条最简单的SELECT,确认连接和Schema没问题,再逐步确认表、字段、语法,一层层缩小范围,而不是盯着那条复杂SQL看半天。
6.2 性能问题的排查与调优
数据迁移完成后,性能问题往往会集中出现。最常见的是查询变慢,原因一般有几个:
第一,统计信息缺失。数据量大的表导入后,达梦的优化器没有统计信息,生成的执行计划可能走了全表扫描。解决办法是导入后立即更新统计信息:
sql复制CALL SP_SYNC_STATISTICS('APP');
其中APP是Schema名。也可以调用DBMS_STATS.GATHER_SCHEMA_STATS('APP')做更细粒度的统计收集。
第二,索引缺失。DTS迁移表结构时会把MySQL里的索引一起迁过来,但如果建表脚本是手工改造的,很容易漏掉索引。建议迁移后对照MySQL的information_schema.statistics,把唯一索引、普通索引都对比一遍。漏索引这个问题在手工迁移时特别常见,我用脚本对比了一遍才发现有六七个索引漏了。
第三,驱动配置问题。应用连MySQL用com.mysql.cj.jdbc.Driver,连达梦要换成dm.jdbc.driver.DmDriver,URL从jdbc:mysql://变成jdbc:dm://。如果应用配置里没换对,连接会失败;如果URL端口写错,也会卡在连接阶段。另外连接池的配置也要检查,达梦默认最大连接数、空闲超时这些参数和MySQL不一样,直接复用MySQL那套连接池配置可能会触发连接泄漏或会话数不够。
第四,查询SQL里的隐式转换。比如MySQL里字符型和数字型的比较会自动转换,达梦在某些场景下会更严格,会导致索引失效。排查方法是用达梦的执行计划查看SQL,确认谓词条件没有走函数转换。我在一个订单查询里发现,WHERE order_no = 12345这种写法,如果order_no是VARCHAR类型,MySQL会隐式转换成数字,达梦里可能直接报类型不匹配,或者走了全表扫描。改成order_no = '12345'后就正常了。
6.3 迁移过程中的注意事项与实操心得
最后分享几个实操心得,都是踩过坑后的体会。
第一,DTS迁移大数据量时,建议把大表单独建任务,小表合并到一个任务里跑。这样任何一个任务失败都不会影响其他表,重试成本低。像我们最大的订单流水表接近2亿行,单独跑了一个晚上;其他一百多张小表合并成四个任务,每个跑十几分钟就结束了。
第二,迁移完成后,先别急着删MySQL的库。保留源库至少一个完整业务周期,以便做数据对比和回切。万一达梦侧有什么查不到的数据,还能回到源库去核对。有些项目上线比较急,第二周就想把源库回收掉,我一般会劝一劝,至少等业务稳定运行一个月再考虑回收。
第三,应用侧的SQL规范要提前定好。很多迁移后的问题不在数据库层,而在SQL写法上。比如MySQL的隐式类型转换、三值逻辑,应用代码里如果大量使用了JOIN ON + WHERE的混合过滤,达梦下执行结果可能和MySQL有细微差异。上线前最好把核心查询都在达梦上跑一遍回归,尤其是那种历史遗留的长SQL,别指望一次就能跑出相同结果。
第四,如果条件允许,做一次小规模的性能压测,对比迁移前后核心接口的响应时间。达梦的参数默认值偏向Oracle风格,不是为MySQL业务默认调优过的。常用的几个调优项包括:BUFFER_POOL_SIZE(缓冲区大小)、MAX_SESSIONS(最大连接数)、UNDO_RETENTION等,这些参数在dm.ini里改,重启服务生效。我这次迁移后把BUFFER_POOL_SIZE从默认值调到了物理内存的40%左右,核心查询的响应时间才算真正达标。
到这里,MySQL迁移到达梦的核心流程就说完了。从我的实际项目经验来看,迁移本身的难点不在搬数据,而在改语法。数据搬运工具能解决80%的问题,剩下20%的存储过程、触发器和特殊函数适配,靠的是细心和排查。如果你正准备做类似迁移,先把源库对象清单盘清楚,再决定用DTS还是手工脚本,然后照着前面说的类型映射和函数替换表逐项核对,整个流程走下来会顺畅很多。
我个人最喜欢的一个习惯是:迁移过程中随时记录问题清单。每遇到一个报错、每次做一个函数替换,都记录到一张表里。等项目结束,这份清单就是团队最宝贵的运维资产,下次迁移同类数据库,直接查单办事。希望这篇文章能帮你在国产化迁移的路上少踩几个坑,顺利交付。
