1. MySQL到达梦数据库迁移概述
最近在帮客户做MySQL到达梦数据库(DM)的迁移项目,过程中遇到了不少SQL语法兼容性问题。作为国产数据库的代表,达梦在语法设计上虽然对MySQL做了大量兼容适配,但两者在底层架构和功能实现上仍有显著差异。这次迁移涉及一个中等规模的电商系统,包含14张业务表、3个视图、4个存储过程和若干触发器,数据量约4000条记录。
提示:达梦数据库初始化时务必注意CASE_SENSITIVE参数设置,MySQL默认大小写不敏感(UTF8_GENERAL_CI),建议达梦设置为CASE_SENSITIVE=0。这个参数一旦设置后续无法修改,需要提前规划好。
2. 迁移前的关键技术准备
2.1 数据库架构差异分析
MySQL采用单实例多库架构,而达梦是单库多模式设计。这意味着:
- MySQL中的每个database需要对应达梦中的一个独立用户和表空间
- 权限体系完全不同,达梦需要为每个迁移用户单独授权
- 元数据查询方式差异显著(如MySQL的information_schema vs 达梦的ALL_OBJECTS)
2.2 兼容性参数配置
在达梦数据库初始化时,这几个关键参数直接影响MySQL兼容性:
-- 必须设置的参数 COMPATIBLE_MODE=4 -- 启用MySQL语法兼容模式 ORDER_BY_NULLS_FLAG=2 -- NULL值排序方式兼容MySQL MY_STRICT_TABLES=1 -- 数据超长时报错(兼容MySQL严格模式)实测发现,未设置COMPATIBLE_MODE时,约60%的MySQL特有语法会报错。设置后这个比例下降到15%左右。
2.3 迁移工具选型
达梦官方提供了三种迁移工具:
- DTS图形化工具:适合中小规模迁移,支持自动类型转换
- DEM评估系统:可生成兼容性报告,提前识别问题点
- SQLark命令行工具:适合批量自动化迁移
我们最终选择DTS+DEM组合方案,先用DEM生成兼容性报告,再通过DTS执行迁移。对于DEM标记为不兼容的对象,提前准备改写方案。
3. 典型SQL语法问题及解决方案
3.1 日期函数差异
问题场景:MySQL的PERIOD_DIFF函数在达梦中不存在
-- MySQL原生语法 SELECT PERIOD_DIFF(202101,202001); -- 返回12 -- 达梦兼容方案 SELECT MONTHS_BETWEEN( TO_DATE(202101,'yyyymm'), TO_DATE(202001,'yyyymm') ); -- 同样返回123.2 分组统计函数
问题场景:YEARWEEK函数在业务报表中广泛使用
-- MySQL写法 SELECT YEARWEEK('2019-07-11',1); -- 返回201928 -- 达梦改写方案 SELECT YEAR('2019-07-11')||WEEK('2019-07-11',1); -- 拼接得到2019283.3 变量累加实现
问题场景:MySQL使用会话变量实现累加
-- MySQL累加写法 SELECT id, name, age, (@age:=@age + age) as age_sum FROM user, (SELECT @age := 0) s ORDER BY id; -- 达梦分析函数方案 SELECT id, name, age, SUM(age) OVER (ORDER BY id) AS age_sum FROM user;3.4 分页查询差异
问题场景:LIMIT/OFFSET语法在达梦中不支持
-- MySQL分页 SELECT * FROM orders LIMIT 10 OFFSET 20; -- 达梦分页方案 SELECT * FROM orders WHERE ROWNUM BETWEEN 21 AND 30; -- 或者使用分析函数 SELECT * FROM ( SELECT ROW_NUMBER() OVER(ORDER BY order_id) AS rn, * FROM orders ) WHERE rn BETWEEN 21 AND 30;4. 完整迁移实施流程
4.1 环境准备阶段
创建专用表空间:
CREATE TABLESPACE "ecshop" DATAFILE '/dmdata/ecshop.dbf' SIZE 2048;创建迁移用户并授权:
CREATE USER "ecshop" IDENTIFIED BY "Dameng123" DEFAULT TABLESPACE "ecshop" DEFAULT INDEX TABLESPACE "ecshop"; GRANT "PUBLIC","RESOURCE","SOI","VTI" TO "ecshop";
4.2 使用DTS工具迁移
连接配置要点:
- MySQL驱动建议使用mysql-connector-java-8.0.xx
- 勾选"保持对象名大小写"选项
- 设置字符集映射:utf8 → UTF-8
数据类型映射调整:
MySQL类型 默认映射 建议映射 TINYINT SMALLINT TINYINT LONGTEXT TEXT CLOB DATETIME DATETIME TIMESTAMP 大表迁移策略:
- 先迁移结构,再迁移数据
- 设置"一次读取行数"为1000
- "一次提交行数"设置为500
4.3 迁移后校验
对象数量校验:
-- MySQL端统计 SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='ecshop'; -- 达梦端统计 SELECT COUNT(*) FROM ALL_OBJECTS WHERE OWNER='ECSHOP' AND OBJECT_TYPE='TABLE';数据一致性校验:
-- 创建校验辅助表 CREATE TABLE verify_result ( table_name VARCHAR(100), src_count INT, dst_count INT, diff INT ); -- 使用存储过程批量校验 BEGIN FOR tbl IN (SELECT TABLE_NAME FROM ALL_TABLES WHERE OWNER='ECSHOP') LOOP EXECUTE IMMEDIATE 'INSERT INTO verify_result SELECT '''||tbl.TABLE_NAME||''', (SELECT COUNT(*) FROM MYSQL_ECSHOP.'||tbl.TABLE_NAME||'), (SELECT COUNT(*) FROM ECSHOP.'||tbl.TABLE_NAME||'), (SELECT COUNT(*) FROM MYSQL_ECSHOP.'||tbl.TABLE_NAME||') - (SELECT COUNT(*) FROM ECSHOP.'||tbl.TABLE_NAME||')'; END LOOP; END;
5. 性能优化实践
5.1 索引重建策略
迁移完成后需要重建索引以提高查询效率:
-- 生成重建索引脚本 SELECT 'REBUILD INDEX '||OWNER||'.'||INDEX_NAME||';' FROM ALL_INDEXES WHERE OWNER='ECSHOP'; -- 对大表采用在线重建 REBUILD INDEX ECSHOP.IDX_ORDER_DATE ONLINE;5.2 统计信息更新
-- 全模式统计信息收集 DBMS_STATS.GATHER_SCHEMA_STATS( 'ECSHOP', -- 模式名 100, -- 采样百分比 FALSE, -- 是否并行 'FOR ALL COLUMNS SIZE AUTO' -- 直方图策略 ); -- 针对大表的统计信息收集 DBMS_STATS.GATHER_TABLE_STATS( 'ECSHOP', -- 模式名 'ORDER_MAIN', -- 表名 NULL, -- 分区名 100, -- 采样百分比 TRUE, -- 级联收集索引统计 'FOR ALL COLUMNS SIZE AUTO' );5.3 SQL调优案例
问题SQL:
SELECT * FROM orders WHERE DATE_FORMAT(create_time,'%Y-%m')='2023-01';优化方案:
-- 方案1:使用达梦兼容函数 SELECT * FROM orders WHERE TO_CHAR(create_time,'yyyy-mm')='2023-01'; -- 方案2:更优的日期范围查询 SELECT * FROM orders WHERE create_time >= TO_DATE('2023-01-01','yyyy-mm-dd') AND create_time < TO_DATE('2023-02-01','yyyy-mm-dd');6. 应用层改造要点
6.1 JDBC连接配置
# 原MySQL配置 # jdbc.url=jdbc:mysql://localhost:3306/ecshop # 达梦新配置 jdbc.url=jdbc:dm://localhost:5236?schema=ECSHOP jdbc.driver=dm.jdbc.driver.DmDriver jdbc.dialect=org.hibernate.dialect.DmDialect6.2 MyBatis映射调整
<!-- 日期处理兼容 --> <resultMap type="Order"> <result column="create_time" property="createTime" jdbcType="TIMESTAMP" typeHandler="org.apache.ibatis.type.DateTypeHandler"/> </resultMap> <!-- 分页查询改造 --> <select id="findByPage" resultMap="Order"> SELECT * FROM ( SELECT ROW_NUMBER() OVER(ORDER BY create_time DESC) AS rn, * FROM orders WHERE user_id = #{userId} ) WHERE rn BETWEEN #{start} AND #{end} </select>6.3 事务处理差异
MySQL默认REPEATABLE_READ隔离级别,而达梦默认READ_COMMITTED。需要在应用启动时显式设置:
// Spring Boot配置示例 @Bean public PlatformTransactionManager transactionManager(DataSource dataSource) { DmTransactionManager manager = new DmTransactionManager(); manager.setDataSource(dataSource); manager.setDefaultTimeout(30); manager.setDefaultIsolationLevel( TransactionDefinition.ISOLATION_REPEATABLE_READ); return manager; }7. 常见问题排查指南
7.1 中文乱码问题
现象:迁移后中文字符显示为问号
解决方案:
- 检查数据库初始化字符集是否为UTF-8
- 确认JDBC连接字符串添加了
?charset=UTF-8 - 验证操作系统locale设置:
export LANG=zh_CN.UTF-8
7.2 自增列异常
现象:插入数据时自增主键冲突
处理方法:
-- 查询当前序列值 SELECT IDENT_CURRENT('ECSHOP.USER_INFO'); -- 重置序列值 ALTER TABLE ECSHOP.USER_INFO MODIFY COLUMN user_id RESTART WITH 1000;7.3 性能下降分析
排查步骤:
- 开启SQL日志监控
-- 设置sqllog.ini [SQLTRACE] SQL_TRACE_MASK = 2:3:22:23:25:28 MIN_EXEC_TIME = 500 - 使用达梦性能监控工具分析TOP SQL
- 对慢SQL执行EXPLAIN PLAN分析
8. 迁移后的维护建议
- 定期统计信息收集:创建每周全量统计信息收集任务
- 备份策略配置:建议采用"全备+增量"的备份方案
- 兼容性测试:每次达梦版本升级后,需重新测试关键业务SQL
- 性能基线管理:记录迁移后的性能指标作为基准
通过这次迁移实践,我深刻体会到不同数据库系统间的语法差异需要细致处理。建议在项目初期就进行全面的兼容性评估,建立完善的测试用例,特别是对复杂业务逻辑和报表查询要重点验证。达梦作为国产数据库的优秀代表,在兼容性方面已经做了大量工作,但完全无缝的迁移仍需要DBA和开发人员的紧密配合。