MySQL迁移到达梦数据库DM8全流程实战:从环境初始化到SQL适配

发布时间:2026/9/18 11:27:30
MySQL迁移到达梦数据库DM8全流程实战:从环境初始化到SQL适配 最近接手了一个从 MySQL 迁移到达梦数据库DM8的项目整个过程跑下来踩了不少坑也理顺了不少之前没想明白的东西。这类国产化迁移项目现在越来越多但网上的资料大多停留在安装达梦数据库使用迁移工具这种单点说明层面真正把全流程怎么走通讲透的文章并不多。我写这篇东西就是想把这次从评估、环境准备、结构迁移、数据迁移到应用适配的完整过程记录下来给后面要做 MySQL 迁移达梦的同学一条能直接照着走的路。这次迁移的背景很简单一套业务系统原本跑在 MySQL 8.0 上因为信创要求需要替换到达梦 DM8。系统不算特别庞大总共 300 多张表、单表最大 2000 万行总数据量大约 200GB 左右应用是基于 Spring Boot 2.x MyBatis-Plus 的微服务属于典型的 Java 系业务系统。听起来不算复杂但真正做起来就会发现MySQL 到达梦的墙远不止换一个 JDBC 驱动那么简单SQL 语法、字段类型、事务隔离、大小写规则、连接参数每个环节都有可能让你折腾一整天。在动手之前我做了一个简单的现状盘点也正因为这个盘点后面才少走了很多弯路。下面我把整个过程拆开来讲重点放在那些常规文档不会写、但实际迁移几乎必踩的环节上。1. 迁移前必须想清楚的几件事模式、字符集和大小写规则很多人一上来就装达梦、建库、跑迁移工具结果跑到一半发现字段乱码、表找不到、SQL 报错最后全盘推倒重来。核心原因就是没在迁移前把达梦的底层行为逻辑搞明白——它和 MySQL 在很多底层规则上完全不一样。1.1 达梦的实例—表空间—用户—模式层级关系MySQL 的逻辑很简单实例下面就是 databaseschema用户和库之间没有强绑定关系。达梦不一样它的结构是实例下面有表空间表空间承载用户用户默认对应一个同名模式SCHEMA模式里面才是表、视图、索引这些对象。默认情况下你在达梦里创建一个用户系统会自动创建一个同名模式。这个差异最直接的后果就是MySQL 里库名.表名的访问方式到达梦要改成用户名.表名或者模式名.表名。如果你的系统里大量使用了dbname.tablename这种全限定名迁移后要么在达梦里建一个和原库同名的用户来兼容要么改应用代码。我这次选的是前者——创建了一个和原 MySQL 库名一致的用户这样应用层改动最小。1.2 大小写敏感达梦默认规则和 MySQL 截然不同MySQL 在 Windows 上默认大小写不敏感Linux 上lower_case_table_names为 0 时大小写敏感但绝大多数业务系统在建表时都会刻意避开大小写混用问题。达梦默认是大小写敏感的而且它对未加引号的标识符会统一转为大写存储。举例来说MySQL 里你写select * from user_info表名存进去是小写user_info达梦里这句 SQL 实际会解析为select * from USER_INFO如果你迁移时表名保留了小写那么这条 SQL 必然报无效的表名。处理办法有两个一是初始化实例时把大小写敏感参数关掉大小写敏感选择N二是迁移时把 DDL 里的表名字段名全部转成大写。我强烈建议选第一种——在 dminit 初始化实例时直接设置大小写不敏感后面省掉无数 SQL 兼容问题。这个决定越早做越好因为实例初始化完成后这个参数就不可修改了。1.3 字符集和存储单位UTF-8 与字符 vs 字节的差异MySQL 的 utf8mb4 是很多系统的默认字符集达梦里对应的字符集是 UTF-8。光这一点还好让人头疼的是 varchar 长度单位的差异MySQL 的varchar(255)表示 255 个字符达梦默认的varchar(255)在 UTF-8 字符集下表示 255 个字节一个汉字占 3 字节也就是说你原来能存 255 个汉字的地方到达梦只能存 85 个汉字。这种问题不会在迁移时报错而是会在数据写入时爆字符串长度超出限制。所以在建表阶段就必须全局搜索 varchar 字段按最大可能的字符数统一增加长度我的做法是字符数小于等于 85 的断然大改整体按原来的字符数乘以 3 来定义达梦中的 varchar 长度比如 MySQLvarchar(2000)改成达梦varchar(6000)。注意达梦 varchar 上限是 8188 字节超过这个就要用 TEXT 或 CLOB 了。2. 环境搭建与初始化参数一句话说清 dminit 和关键配置达梦的安装本身不复杂Linux 上解压安装包执行DMInstall.bin图形界面或者命令行模式都可以。真正关键的是安装完成后用dminit初始化实例时的参数设置这些参数决定了数据库底层行为初始化之后想改基本不可能。我这次用的初始化命令格式大致如下Linux 环境安装路径为 /opt/dmdbmscd /opt/dmdbms/bin ./dminit PATH/dm/data PAGE_SIZE32 EXTENT_SIZE32 CASE_SENSITIVEN CHARSET1 DB_NAMEDMDB INSTANCE_NAMEDMSERVER PORT_NUM5236几个关键参数逐一说明PATH/dm/data实例数据文件存放目录必须提前创建。PAGE_SIZE32页大小 32KB。页大小决定了单行最大长度如果数据量大有 text/blob 字段页太小容易报错。模块化系统建议直接上 32K。EXTENT_SIZE32簇大小按默认 32 即可。CASE_SENSITIVEN大小写不敏感。这项务必设成 N理由上面已经说过。CHARSET11 表示 UTF-8这个不用纠结直接写 1。DB_NAMEDMDB库名。INSTANCE_NAMEDMSERVER实例名默认即可。PORT_NUM5236达梦默认端口。初始化完成后还会用到两个工具DmServiceDMSERVER start启动实例disql是达梦的命令行客户端类似 MySQL 的 mysql 命令。后续建用户、导数据、跑脚本都靠它。关于兼容模式达梦支持的COMPATIBLE_MODE参数可以调整部分 SQL 语法兼容性常见取值有 0Oracle 模式、1SQL Server 模式、2MySQL 模式、3PG 模式等。我建议设成 MySQL 模式COMPATIBLE_MODE2这样limit语法、ifnull函数等可以直接用。但这不代表所有 MySQL 语法都支持后面适配环节仍需逐个验证。3. 表结构迁移实战类型映射、自增列和 DDL 改造细节结构迁移是最考验细心程度的一步。表面上看着就是建表嘛实际上 MySQL 和达梦 DDL 的差异会让自动生成的建表脚本报出一堆错。我用达梦 DTS数据迁移工具自动生成了一版建表语句然后手动逐项修正比直接手工写一遍要快很多。3.1 数据类型映射表这是我最终使用的对应关系MySQL 类型达梦类型注意事项tinyint / tinyint(1)SMALLINT / TINYINTtinyint(1) 常用于布尔达梦可用 TINYINT应用层自行映射 booleansmallintSMALLINT直接对应int / integerINT直接对应bigintBIGINT直接对应varchar(n)VARCHAR(n*3)由于字节存储规则必须按字符最大长度乘 3 处理char(n)CHAR(n*3)同理但 char 是定长建议改 varchar 省空间textTEXT达梦 TEXT 类型可以也可用 CLOBlongtextCLOB直接映射为 CLOBblobBLOB直接映射为 BLOBdatetimeTIMESTAMP达梦没有 datetime用 timestamp 等价timestampTIMESTAMP注意默认值语法不同dateDATE直接对应decimal(p,s)DECIMAL(p,s)直接对应float / doubleFLOAT / DOUBLE直接对应jsonTEXT / VARCHAR达梦没有原生 json 类型建议用 TEXT 并让应用自行序列化enumVARCHAR CHECK 约束或者直接改为应用层枚举校验bit(1)BIT注意和 tinyint(1) 区分特别注意text、JSON 这类字段如果出现在索引里MySQL 允许前缀索引idx (content(100))达梦不支持这种写法迁移前必须把这类索引去掉改为应用层处理或删除该索引。3.2 自增列的处理AUTO_INCREMENT 改成 IDENTITYMySQL 建表最常见的id bigint auto_increment primary key在达梦里不能用要改成CREATE TABLE user_info ( id BIGINT IDENTITY(1,1) PRIMARY KEY, user_name VARCHAR(300) NOT NULL );IDENTITY(1,1)表示自增起始值和步长都是 1。如果原表有指定自增起始值比如AUTO_INCREMENT 10000达梦里可以通过设置IDENTITY_INSERT为 ON 后插入指定值来重置种子或者在建表时不指定初始值导完数据后用专门的系统函数调整。不过通常业务不会依赖自增起点保持默认即可。批量修改自增列时有个小技巧先用 SQL 查 information_schema.columns 找出所有含 auto_increment 的表然后脚本化处理不要手工一张张改。我是用 Python 脚本配合正则做的200 多张含自增列的表几分钟就改完了。3.3 DDL 中的其他雷区MySQL 表尾常带ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_general_ci达梦直接不识别要删掉。ON UPDATE CURRENT_TIMESTAMP这个属性达梦不支持需要先删除由应用层维护更新时间字段或者触发实现。UNIQUE KEY和KEY的混合定义达梦语法要求索引名唯一有时候 MySQL 里同名索引迁移过去会报重复。外键约束建议先去掉数据迁移完成后再手动补否则大批量导入时约束检查会拖慢速度而且导入顺序稍有不对就报约束冲突。4. 数据迁移实操DTS 工具、断点续传和千万级大表处理结构迁移完成下一步就是灌数据。达梦自带的数据迁移工具 DTSDM Data Transformation Service是首选图形化界面支持从 MySQL 直接迁到达梦。DTS 的位置在安装目录的tool文件夹下启动命令是./dts。4.1 DTS 基本流程与配置细节DTS 的使用比较简单新建工程新建迁移任务源端选 MySQL填 IP、端口、用户、密码目标端选达梦填达梦连接信息然后勾选要迁移的表。需要注意下面几个细节一是源端 MySQL 连接串里字符编码要显式设置。我遇到过用 DTS 迁完后一半中文变成问号的情况原因就是 DTS 读取 MySQL 数据时用的编码与库实际编码不一致。解决方案是在源端 URL 中加上?useUnicodetruecharacterEncodingutf8。二是迁移选项里有一个批量提交相关参数。默认一次提交的行数不大碰到 2000 万行的大表会非常慢而且如果中途断了已迁移的数据和未迁移的数据混在一起很难定位。我通常把大批量表单独处理一次提交 500~1000 行同时加大 DTS 的客户端内存。三是大字段表迁移要特别小心。包含 BLOB/CLOB 的表建议拆出来单独迁不然 DTS 在大字段密集的场景下经常报内存溢出。4.2 我遇到的大表迁移方案对于千万级大表DTS 图形化界面的进度条很不好用而且一旦任务中途失败整个任务要重新来没有断点续传的概念。我改用了更可控的方案DTS 迁移结构 → 手动分批导数据。具体操作是在 MySQL 端用select ... into outfile导成 CSV然后用达梦命令行工具dmfldr批量加载。dmfldr是达梦的批量加载工具速度和稳定性远好于 DTS 图形界面。常用命令大致如下cd /opt/dmdbms/bin ./dmfldr useridSYSDBA/SYSDBAlocalhost:5236 control/data/load/user_info.ctl控制文件user_info.ctl的内容如下LOAD DATA INFILE /data/export/user_info.csv INTO TABLE user_info FIELDS TERMINATED BY , OPTIONALLY ENCLOSED BY TRAILING NULLCOLS几个参数说明INFILE指定源 CSV 路径INTO TABLE指定目标表FIELDS TERMINATED BY ,因为是 CSV 所以逗号分隔TRAILING NULLCOLS表示源文件缺失的列视为 NULL。这些组合起来基本能覆盖大部分业务场景。用dmfldr导入 2000 万行的测试结果我记录一下单表 10GB 左右数据大概 8 分钟跑完比 DTS 图形界面快了 5 倍以上而且不会因为连接空闲超时中断。如果你数据量也不小我建议直接放弃 DTS 灌数据这条路径改用导出 CSV dmfldr省下的时间可以多干好多事。4.3 迁移后的数据校验对账是底线数据导完后必须做一致性校验。我的做法是在 MySQL 和目标达梦里各执行一段对账 SQL比对三组指标总行数逐表select count(*)对比。关键聚合值对金额、数量类字段select sum(xxx)对比。随机抽样取主键 id 模 100 的数据逐字段对比字符串长度和拼接哈希。如果对不上优先检查字符集和varchar长度问题因为超长截断是迁移后数据不一致最常见的来源。前面提到 varchar 按字节算的问题迁移工具不会报错它会悄悄截断这种数据对账才能发现。5. 应用层适配驱动、连接串、连接池和 SQL 方言改造数据进去了表结构也对了接下来就是让人最头疼的应用改造环节。微服务多、SQL 分散、各种隐式写法如果没有提前摸底这里的排查成本会非常大。我没有全部堆到迁移后期才处理而是在结构迁移后就让开发团队先跑起来边跑边报错边改效率高很多。5.1 JDBC 驱动和连接串达梦提供 JDBC 驱动常见的 jar 包名为Dm8JdbcDriver18.jar或者DmJdbcDriver18.jar名称可能因版本而异。替换掉原 MySQL 驱动同时连接串也要改// 修改前MySQL jdbc:mysql://192.168.1.10:3306/business_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai // 修改后达梦 jdbc:dm://192.168.1.20:5236/business_db注意连接串格式是jdbc:dm://IP:端口/模式名最后这个路径是模式名而不是达梦的库名。如果你的应用里访问的是business_db并且达梦侧也建了同名用户那么这个串是有效的。5.2 HikariCP 连接池参数调整我们的项目用的是 HikariCP 连接池对应的application.yml数据库配置最终调整成了这样spring: datasource: driver-class-name: dm.jdbc.driver.DmDriver jdbc-url: jdbc:dm://192.168.1.20:5236/business_db username: business_user password: your_password hikari: minimum-idle: 5 maximum-pool-size: 20 connection-test-query: SELECT 1 FROM DUAL connection-timeout: 30000 validation-timeout: 5000 idle-timeout: 600000 max-lifetime: 1800000这里面最关键的是connection-test-query。HikariCP 默认用连接测试语句来检测连接是否存活MySQL 下通常是SELECT 1达梦下建议写成SELECT 1 FROM DUAL因为达梦默认的兼容模式可能不认为SELECT 1是一条验证查询语句。实际验证发现SELECT 1在达梦 MySQL 兼容模式下也能跑通但为了稳妥改成SELECT 1 FROM DUAL是最保险的。早期版本还踩过连接池报connection is closed的问题调整validation-timeout和connection-timeout后恢复正常。5.3 SQL 方言改造高频差异清单这是应用改造的重头戏。下面这些差异是我这次迁移中实际遇到并修改过的按出现频率从高到低列出来MySQL 写法达梦兼容写法说明limit 0, 10limit 0, 10或top(10)达梦 MySQL 兼容模式支持 limitOracle 模式不支持ifnull(a, b)ifnull(a, b)或nvl(a, b)两种都支持建议统一 nvlconcat(a, b, c)concat(a, b, c)MySQL 兼容模式 OKOracle 模式用 agroup_concat(x)listagg(x, ,)达梦没有 group_concat改用 listaggdate_format(now(), %Y-%m-%d)to_char(sysdate, yyyy-mm-dd)两者兼容性差异较大按模式调整xxxXXX反引号不要用改为双引号或不加引号on duplicate key updatemerge into ...达梦不支持该语法改用 mergereplace into类似自查 更新/插入无直接等价语法insert ... values (...),(...)多行插入同样支持MySQL 兼容模式分批插入时注意长度限制now()sysdate或current_timestamp兼容模式下 now() 也可用update A join B ...改写为子查询或 merge达梦不支持 update join必须拆分delete A from A join B ...改写为子查询同上其中最坑的是update ... join和delete ... join这类语句在达梦里完全不支持而且 MyBatis 的 XML 里往往写得比较隐蔽。我的办法是全量搜索 XML 文件里的join逐个手工改写。5.4 MyBatis-Plus 和分页插件的适配问题我们用了 MyBatis-Plus它自带的分页插件底层是生成LIMIT ?,?这样的分页 SQL。达梦 MySQL 兼容模式下能识别但有个隐藏问题分页计数时达梦对count(distinct xxx)和复杂子查询的支持不如 MySQL 稳定偶尔会出现性能骤降。建议把分页插件调整成达梦方言或者直接使用 MyBatis-Plus 内置的DbType.DM。如果项目里用的是 PageHelper设置helper-dialectdm可以解决大部分分页 SQL 适配问题。5.5 Nacos 配置中心适配达梦的额外工作微服务如果用了 Nacos还需要处理 Nacos 自身数据源替换的问题。Nacos 默认用 MySQL 存配置和服务发现数据到达梦环境需要单独处理。我用的是 Nacos 2.2.3 版本官方在这个版本中对达梦正式做了适配但需要额外引入达梦数据库插件依赖并在application.properties里做数据源配置切换。这个环节和业务 SQL 改造是独立的但如果不提前搞服务启动时 config 模块会起不来联调阶段会很被动。建议在应用改造的同时就把 Nacos 侧的数据源也切到达梦上测试环境尽早暴露问题。6. 迁移过程出现的错误号 -3236一次典型的排错链路复盘整个迁移过程中最让人头大的错误是在集成测试阶段出现的应用日志里频繁打出迁移达梦错误号: -3236一开始完全没有头绪因为这条错误既不是连接异常也不是语法错误错误信息很简短指向性不强。6.1 第一次定位错误号本身的含义达梦的错误号体系比较特殊负数的错误号对 DBA 来说是家常便饭但对应用开发来说并不直观。我先查了达梦安装目录下的dmdbms/doc里的错误码手册同时用 disql 做了个简单的验证通过SP_SET_ERR_LOG或者直接查系统视图可以拿到详细的错误堆栈。错误号 -3236 在达梦里的典型含义是非法的日期时间类型数据或者说日期时间转换失败之类的运行时错误。但只有这个信息还不够因为报错的 SQL 可能是任何一条包含日期比较条件的查询语句。6.2 第二次定位把报错的 SQL 从应用日志里完整捞出来关键一步是把应用日志级别改为 DEBUG把 MyBatis 的执行 SQL 打印完整。改完后重现问题发现报错的 SQL 类似SELECT * FROM business_order WHERE order_status 2 AND create_time 2024-04-01 00:00:00这条 SQL 在 MySQL 里跑了几万次都没问题到了达梦就报 -3236。于是重点转向 create_time 这个字段——在 MySQL 它是datetime类型迁移时我们改成了达梦的TIMESTAMP类型。6.3 找到根因字符格式的日期字符串在达梦 TIMESTAMP 上的隐式转换规则差异MySQL 对字符串转日期的容忍度相当高2024-04-01 00:00:00这种标准格式没问题但如果数据库里存了2024-4-1 0:0:0这种非严格格式的字符串MySQL 也能自动解析。达梦对隐式转换要求更严格部分场景下要求字符串严格匹配数据库默认日期格式不匹配就会抛转换失败。问题出在迁移的数据本身。业务表 create_time 字段通过 DTS 从 MySQL 迁过来其中一批历史数据因为历史原因存的是2024-4-1 0:0:0这种非标准格式。在 MySQL 中它被当作 datetime 正常存储查询时也正常到达梦后这一批数据被当成字符串插入到 TIMESTAMP 字段虽然插入成功了达梦宽松传入但查询时一旦走到严格比较就会触发 -3236。6.4 解决方案和数据清洗解决上有两条路第一是把异常数据批量清洗为标准格式第二是调整达梦的日期格式参数。我这次选择了前者因为元数据不干净迟早是隐患。具体操作如下在达梦的 disql 里执行UPDATE business_order SET create_time TO_TIMESTAMP(2024-04-01 00:00:00, yyyy-mm-dd hh24:mi:ss) WHERE create_time IS NOT NULL AND TO_CHAR(create_time, yyyy-mm-dd hh24:mi:ss) NOT LIKE ____-__-__ __:__:__;这样先把非标准数据清洗掉再持续观察一周-3236 不再出现。后来我复盘时总结出一个经验这类错误号问题不要先去翻错误码表死磕先把报错的完整 SQL 捞出来再对比 MySQL 与达梦在隐式类型转换上的差异往往一两轮就能定位。6.5 类似问题从 -3236 延伸到其他类型转换隐患被 -3236 教训了一次后我用同样的思路排查了其他可能存在的隐式转换问题额外发现了两处隐患一是字符串和数字拼接时的类型转换。MySQL 中WHERE phone 13812345678会自动把数字转字符串再比较达梦中如果电话号码字段是 VARCHAR直接用数字比较在某些模式下会走索引失败或报类型转换错误需要显式加引号写成WHERE phone 13812345678。二是布尔值的表示。MySQL 中tinyint(1)常见且可以直接和true/false比较达梦如果保持 TINYINT 类型ORM 框架可能把它解析为数字而不是布尔值导致 where 条件拼出where flag true而达梦无法识别需要改为where flag 1。这些问题的共性是MySQL 的隐式类型转换太宽容掩盖了很多脏数据迁到达梦后这些灰产全浮出水面。迁移项目表面上是数据库切换实际上也是一次数据质量治理。7. 迁移后期的性能验证与一些提醒数据迁移完成、应用跑通之后还需要做一轮全面的性能回归。重点看几个场景批量导入接口、报表统计查询、分页列表、多表关联。这次迁移中出现过一个比较典型的性能陷阱某条 MySQL 下 30ms 的报表 SQL 到达梦跑了 8 秒原因是一条子查询里用了GROUP_CONCAT改造为LISTAGG后发现达梦对LISTAGG的开窗排序场景优化一般最后通过改写为两个子查询 应用层拼接才解决。这种性能问题没有捷径只能逐个慢 SQL 过一遍执行计划调整索引和 SQL 写法。达梦的执行计划查看可以用EXPLAIN关键字常用且直观。值得提醒的是达梦索引的命名规则和 MySQL 不同迁移后索引名如果过长可能被截断需要检查一下。另外如果你所在项目未来还要做 Oracle 目标库的迁移那这次到达梦的很多改造经验可以复用大半因为达梦的体系更接近 Oracle。反过来如果只做 MySQL 到达梦这一趟建议在阶段评审时把改造点记录成清单方便后续审计和复盘。这次迁移从开始到业务全量上线差不多用了三周其中结构改造和数据迁移只占了一周剩下两周几乎全部耗在应用 SQL 适配和 -3236 这类隐藏问题排查上。如果让我再做一次我一定会在迁移前用慢查询日志加上全量 SQL 扫描把 MySQL 的隐式转换、不标准日期、update join 这类写法提前暴露出来能省掉一半的联调时间。最后一个小技巧达梦的 disql 里可以开启SET ECHO ON来回显执行过程中的 SQL 日志排查批量脚本问题的时候非常好用。这个细节最初没人告诉我连蒙带猜试出来的写在这里当个彩蛋吧。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询