1. MySQL到达梦数据库迁移实战指南
在国产化替代的大背景下,越来越多的企业开始将业务系统从MySQL迁移至达梦数据库。作为国产数据库的佼佼者,达梦数据库在政务、金融等领域获得广泛应用。但两种数据库在语法、数据类型和功能特性上存在显著差异,这使得迁移过程充满挑战。
我最近刚完成一个电商平台的数据库迁移项目,将超过2TB的MySQL数据完整迁移到达梦DM8。整个过程踩了不少坑,也积累了一些实战经验。本文将详细分享从评估规划到最终验证的全流程操作指南,特别针对数据类型转换、SQL语法改写等关键难点提供解决方案。
2. 迁移前准备工作
2.1 环境评估与兼容性分析
在开始迁移前,必须对源库和目标库进行全面的兼容性评估:
- 版本匹配:MySQL 5.7对应达梦DM7,MySQL 8.0建议使用达梦DM8
- 字符集检查:达梦默认GB18030,需确认是否与MySQL的utf8mb4兼容
- 存储引擎:达梦不支持MyISAM,所有表需转换为InnoDB等效的DM表空间
重要提示:达梦的页大小默认为8KB,与MySQL的16KB不同,这会影响性能评估
2.2 迁移工具选型
根据项目规模可选择不同迁移方案:
| 工具类型 | 代表工具 | 适用场景 | 优缺点 |
|---|---|---|---|
| 官方迁移工具 | DTS | 中小型数据库 | 图形化操作简单,但大表易超时 |
| 商业ETL工具 | Kettle/Informatica | 复杂业务逻辑迁移 | 支持转换规则定制,成本高 |
| 自研脚本 | Python+SQLAlchemy | 特殊需求场景 | 灵活度高,开发成本大 |
我们项目最终选择DTS工具结合手动调整的方案,在保证效率的同时处理特殊对象。
3. 数据结构迁移实战
3.1 表结构转换要点
使用达梦的迁移向导生成DDL时,需要特别注意以下类型的转换:
-- MySQL原结构 CREATE TABLE `orders` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `order_no` varchar(32) COLLATE utf8mb4_bin NOT NULL, `create_time` datetime(3) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `idx_order_no` (`order_no`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; -- 达梦转换后 CREATE TABLE "orders" ( "id" BIGINT IDENTITY(1,1) NOT NULL, "order_no" VARCHAR(32) NOT NULL, "create_time" TIMESTAMP(3) NOT NULL, PRIMARY KEY ("id") );关键差异处理:
- 自增列:达梦使用IDENTITY属性替代AUTO_INCREMENT
- 字符集:达梦默认不指定COLLATE
- 索引:达梦的UNIQUE约束语法不同
3.2 数据类型映射对照
常见数据类型的转换关系:
| MySQL类型 | 达梦对应类型 | 注意事项 |
|---|---|---|
| TINYINT(1) | BOOLEAN | 达梦没有布尔类型模拟 |
| DATETIME(6) | TIMESTAMP(6) | 微秒精度需要显式指定 |
| JSON | TEXT | 需要应用层处理JSON解析 |
| ENUM('Y','N') | CHAR(1) | 达梦无ENUM需用CHECK约束替代 |
对于金额字段,建议统一转换为达梦的DECIMAL(19,4)以避免精度损失。
4. 数据迁移实施步骤
4.1 全量数据迁移
使用达梦DTS工具进行初始全量迁移时,推荐配置:
- 设置批量提交大小为5000行/次
- 启用字符集自动转换
- 对大表启用多线程(不超过CPU核数的2倍)
- 设置网络超时为3600秒
遇到大表(超过1000万行)时,可以采用分片迁移策略:
# 按ID范围分批导出 mysqldump -t -u root -p --where="id>=1 and id<1000000" db1 orders > part1.sql dmimp USERID=SYSDBA/SYSDBA FILE=part1.sql FULL=Y LOG=part1.log4.2 增量数据同步方案
保证业务连续性的关键是在全量迁移后建立增量同步:
- 基于时间戳:适用于所有表都有更新时间字段的场景
- 触发器方案:在MySQL端创建触发器记录变更
- Binlog解析:使用MaxWell等工具解析MySQL二进制日志
我们项目采用方案3,配置示例:
# Maxwell配置示例 producer=rabbitmq rabbitmq_host=192.168.1.100 rabbitmq_user=admin rabbitmq_pass=123456 filter=exclude: *.*, include: db1.orders, include: db1.users5. 应用层适配改造
5.1 SQL语法差异处理
达梦与MySQL的SQL方言主要差异点:
分页查询:
-- MySQL SELECT * FROM orders LIMIT 10 OFFSET 20; -- 达梦 SELECT * FROM orders LIMIT 20, 10;字符串连接:
-- MySQL SELECT CONCAT(first_name, ' ', last_name); -- 达梦 SELECT first_name || ' ' || last_name;日期函数:
-- MySQL SELECT DATE_ADD(NOW(), INTERVAL 1 DAY); -- 达梦 SELECT ADD_DAYS(CURRENT_TIMESTAMP, 1);
5.2 存储过程迁移
达梦的PL/SQL与MySQL存在显著差异:
- 变量声明位置不同(达梦必须在BEGIN前)
- 异常处理语法差异
- 游标操作方式不同
改造示例:
-- MySQL原过程 DELIMITER // CREATE PROCEDURE update_price(IN rate FLOAT) BEGIN DECLARE done INT DEFAULT FALSE; DECLARE cur CURSOR FOR SELECT id,price FROM products; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; OPEN cur; read_loop: LOOP FETCH cur INTO pid, old_price; IF done THEN LEAVE read_loop; END IF; UPDATE products SET price = old_price * rate WHERE id = pid; END LOOP; CLOSE cur; END // DELIMITER ; -- 达梦改写后 CREATE OR REPLACE PROCEDURE update_price(rate FLOAT) AS CURSOR cur IS SELECT id,price FROM products; pid NUMBER; old_price NUMBER; BEGIN OPEN cur; LOOP FETCH cur INTO pid, old_price; EXIT WHEN cur%NOTFOUND; UPDATE products SET price = old_price * rate WHERE id = pid; END LOOP; CLOSE cur; END;6. 迁移后验证与优化
6.1 数据一致性校验
开发校验脚本检查以下关键指标:
- 各表的记录数是否一致
- 数值型字段的SUM/AVG值差异
- 最大最小值边界检查
- 采样数据内容比对
推荐使用以下SQL进行快速校验:
-- 记录数比对 SELECT 'orders' AS table_name, (SELECT COUNT(*) FROM mysql.orders) AS mysql_cnt, (SELECT COUNT(*) FROM dm.orders) AS dm_cnt UNION ALL SELECT 'users', (SELECT COUNT(*) FROM mysql.users), (SELECT COUNT(*) FROM dm.users); -- 数据内容采样检查 SELECT m.id, m.amount, d.amount, ABS(m.amount - d.amount) AS diff FROM mysql.orders m JOIN dm.orders d ON m.id = d.id WHERE ABS(m.amount - d.amount) > 0.01 LIMIT 100;6.2 性能优化调整
达梦特有的优化参数:
-- 调整内存参数 ALTER SYSTEM SET 'MEMORY_TARGET'='8G' SCOPE=SPFILE; -- 优化器配置 ALTER SESSION SET OPTIMIZER_MODE=1; -- 1为成本优化模式 -- 统计信息收集 ANALYZE TABLE orders COMPUTE STATISTICS FOR ALL COLUMNS; -- 重建索引 ALTER INDEX idx_order_no REBUILD ONLINE;特别要注意达梦的锁机制与MySQL不同,在高并发场景下可能需要调整隔离级别:
-- 默认是READ COMMITTED SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;7. 常见问题解决方案
7.1 中文乱码问题
现象:迁移后中文字符显示为问号
解决方案:
- 确认达梦数据库字符集:
SELECT * FROM V$PARAMETER WHERE NAME LIKE '%CHARACTER%'; - 重建数据库使用UTF-8:
createdb -e UTF-8 -l CHINESE_PRC -p 5236 DMDB - 连接字符串添加字符集参数:
jdbc:dm://127.0.0.1:5236?charset=utf8
7.2 自增列冲突
现象:插入数据时自增列报错
处理方法:
- 重置序列值:
-- 获取当前最大值 SELECT MAX(id) FROM orders; -- 重置序列 ALTER SEQUENCE "orders_id_SEQ" RESTART WITH 1001; - 或者使用达梦的GUID替代自增:
CREATE TABLE orders ( id VARCHAR(36) DEFAULT SYS_GUID() PRIMARY KEY, ... );
7.3 大对象迁移异常
处理BLOB/CLOB字段的特殊方法:
- 使用达梦的EXP/IMP工具专门处理:
dmexp USERID=SYSDBA/SYSDBA TABLES=(table_with_blob) \ FILE=blob_data.dmp LOG=exp_blob.log - 或者在应用层分块读写:
// Java示例代码 try (InputStream is = rs.getBinaryStream("file_data")) { OutputStream os = new FileOutputStream("output.dat"); byte[] buffer = new byte[4096]; int len; while ((len = is.read(buffer)) > 0) { os.write(buffer, 0, len); } }
8. 迁移后的长期维护
8.1 监控方案配置
达梦特有的监控视图:
-- 查看锁等待 SELECT * FROM V$LOCK_WAIT; -- 会话监控 SELECT SESS_ID, SQL_TEXT, STATUS FROM V$SESSIONS WHERE USER_NAME='APP_USER'; -- 空间使用 SELECT TABLESPACE_NAME, USED_SIZE/1024/1024 "USED(MB)" FROM DBA_DATA_FILES;建议配置以下告警阈值:
- 表空间使用率 >80%
- 活跃会话数 >CPU核数*5
- 锁等待时间 >30秒
8.2 备份策略调整
达梦的备份恢复命令示例:
# 全量备份 dmrman BACKUP DATABASE '/dmdata/DAMENG/dm.ini' FULL TO BACKUP_FILE BACKUPSET '/backup/full_20230701'; # 增量备份 dmrman BACKUP DATABASE '/dmdata/DAMENG/dm.ini' INCREMENTAL WITH BACKUPDIR '/backup/full_20230701' BACKUPSET '/backup/incr_20230702'; # 恢复示例 dmrman RESTORE DATABASE '/dmdata/DAMENG/dm.ini' FROM BACKUPSET '/backup/full_20230701'; dmrman RECOVER DATABASE '/dmdata/DAMENG/dm.ini' FROM BACKUPSET '/backup/incr_20230702';建议备份策略:
- 每周日全备
- 每日增量备份
- 保留最近4个全备副本
9. 经验总结与建议
经过这次迁移项目,我总结了几个关键经验点:
预迁移评估要充分:至少提前2周进行兼容性测试,特别是存储过程和复杂查询
分阶段实施:先迁移历史数据,再同步增量,最后切换应用连接
建立回退方案:保留MySQL实例至少1个月,准备快速回退脚本
团队培训:达梦的运维方式与MySQL差异大,需要专门培训
对于特别复杂的系统,建议采用双写过渡方案:在应用层同时写入MySQL和达梦,等验证无误后再下线MySQL。这个过程可能会持续2-3个月,但能最大限度保证业务安全。