MIGRATE.md 4.7 KB

DbMigrator — MySQL → 达梦(DM8) 数据迁移工具

单文件 Java 工具(JDK 17 直接运行,无需构建)。结构走 SHOW CREATE TABLE 结构化解析生成达梦 DDL,数据走 JDBC 驱动层 ResultSet → PreparedStatement 批量直搬,全程零 SQL 文本转义(INSERT 转义是 dump 文本转换路线最脆弱的环节,本工具从驱动层消灭它)。

已在本项目测试库实测通过:15 表结构 + 1538 行数据 + 18 外键 + 全部唯一索引,行数/主键水位/大字段/全表指纹校验全部一致。

一、运行命令

# JDK17 + 两个 JDBC 驱动(m2 仓库已有)
MYSQL_JAR=C:/Users/Administrator/.m2/repository/com/mysql/mysql-connector-j/8.4.0/mysql-connector-j-8.4.0.jar
DM_JAR=C:/Users/Administrator/.m2/repository/com/dameng/DmJdbcDriver18/8.1.3.62/DmJdbcDriver18-8.1.3.62.jar

cd backend-java/tools
java -Dfile.encoding=UTF-8 -cp "$MYSQL_JAR;$DM_JAR" DbMigrator.java migrate \
  --source-url 'jdbc:mysql://源库:3306/dms?useSSL=false&characterEncoding=utf8' \
  --source-user dms_app --source-pwd '密码' \
  --target-url jdbc:dm://达梦库:5236 --target-user DMS --target-pwd '密码' \
  --schema DMS_MIGTEST --create-schema --drop-existing

三种模式:

模式 说明
migrate 建结构 + 搬数据 + 建外键 + 自动校验(行数 + 单列数值主键水位)
verify 只校验,不改动目标库
drop-schema 只清空目标模式(先删全部外键再删表,可反复重跑)

常用参数:--tables a,b 只迁指定表;--exclude c 排除表;--batch 1000 批大小;--no-verify 跳过校验;--err-log FILE 坏行日志(默认 migrate_errors.log)。

Windows 注意:若密码含 !,在部分 shell(git bash 传参给 java.exe)下引号包裹反而会被转义成 \!,此时密码不加引号裸写即可;URL 含 & 则必须加引号。

二、生产迁移操作步骤

  1. 预检:先 verify 跑一遍目标连通;确认源库为 MySQL 8.x。
  2. 迁移:migrate --create-schema --drop-existing(幂等,可反复重跑)。
  3. 补齐版本差:本库源数据停在 alembic 0021(缺 0022 登录锁定的 failed_login_count/locked_until 列)。Flyway 在达梦默认禁用,需手工执行 backend-java/src/main/resources/db/migration/V7__login_lockout.sql(幂等),并同步更新 alembic_version/flyway_schema_history 中该库对应的版本记录。
  4. 切换:修改 .env 的 DMS_DATABASE_URL 指向达梦(jdbc:dm 或 dm+dmPython 形式均可,Java 侧自动互转),重启服务。
  5. 回退:.env 切回 MySQL URL 即可(迁移只写目标库,不动源库)。

三、已内建的达梦兼容规则(DM8 8.1.2.128 实测)

MySQL 达梦处理
AUTO_INCREMENT IDENTITY(1,1);数据期自动 SET IDENTITY_INSERT ON/OFF + 显式列清单
生成列 IF(cond,1,NULL) VIRTUAL TINYINT AS (CASE WHEN cond THEN 1 END)
含生成列的内联 UNIQUE(-2034) 后置 CREATE UNIQUE INDEX
NULL 判重语义差异 MySQL 任一键列 NULL 即不判重;达梦(Oracle 语义)仅全 NULL 键不判重。对齐方式:全部键成分套同一守卫 CASE,非判重行整体置全 NULL 键
单标记生成列唯一键(全局唯一默认项) 单成分 CASE 守卫唯一索引
ON DELETE RESTRICT 剥离(默认 NO ACTION 语义等价)
FULLTEXT/SPATIAL/前缀索引 跳过/整列降级,输出 WARN
datetime(n)/timestamp(n) TIMESTAMP(n)
text/mediumtext/longtext/json TEXT/CLOB/TEXT
tinyint(n) TINYINT(n 为显示宽度不代表值域);unsigned 才升 SMALLINT
enum/set VARCHAR(255) + 告警(枚举约束不迁移,靠 CHECK 或应用层)
保留字标识符 统一大写,保留字加双引号

四、韧性机制

  • 分页:单列数值主键用 WHERE pk > ? ORDER BY pk LIMIT n 窗口分页(内存友好);复合/无主键表整表加载并 WARN。
  • 坏行:批次失败自动降级逐行抢救,坏行记 --err-log 不中断整体(唯一键冲突的行会明确记录 pk)。
  • 外键后置:全部数据就位后再建外键,消除表间迁移顺序依赖。

五、告警处理建议

  • 非单列数值主键,整表加载:版本表/字典表无碍;业务大表出现时评估内存或改用 --tables 分批。
  • 多列唯一键含可空列…NULL组合重复的数据将冲突:源库若真存在多行「部分 NULL」重复键(MySQL 合法、达梦冲突),需先在源库清洗,或接受该约束在达梦更严格。
  • UNSIGNED 不被达梦支持:值域超 INT/BIGINT 上限的数据不会存在时无碍;tinyint/smallint unsigned 已自动升档。