Skip to content

7.6 数据库迁移工具 ​

概述

整库迁移器(internal/migrate + cmd/migrator)实现五方言互迁:源库内省 → 中间表示 → 目标方言 DDL → 分批搬运 → 序列修复 → 行数校验。纯 CLI、gdb 动态建连裸读裸写(不走 dao,不触发全局钩子)。

五阶段流程 ​

text
┌────────────┐   ┌────────────┐   ┌────────────┐   ┌────────────┐   ┌────────────┐
│ 1/5 建目标库│──▶│ 2/5 建表结构│──▶│ 3/5 搬运数据│──▶│ 4/5 序列修复│──▶│ 5/5 行数校验│
│ ensureDB   │   │ 内省→建表   │   │ 分批 INSERT│   │ setval等   │   │ COUNT 对比 │
│ (不存在才建)│   │ →建索引    │   │ 进度回调    │   │            │   │ PASS/FAIL  │
└────────────┘   └─────┬──────┘   └─────┬──────┘   └─────┬──────┘   └─────┬──────┘
                 drop-first:        无主键表报错跳过    pgsql/mssql 专项   不一致→
                 先 DropAllTables                   其余方言免修       整体失败
                 非清空: 同名表拦截跳过

CLI 参数 ​

sh
go run ./cmd/migrator \
  -src-type=mysql -src-host=127.0.0.1 -src-hostport=3306 \
  -src-database=easygoadmin.goframe.elevue -src-username=root -src-password=root \
  -dst-type=pgsql -dst-host=127.0.0.1 -dst-hostport=5432 \
  -dst-database=easygoadmin.goframe.elevue -dst-username=postgres -dst-password=123456 \
  -drop-target-first
参数组键默认
源库-src-type / host / hostport / database / username / passwordmysql / 127.0.0.1 / 3306 / — / root / —
目标库-dst-type / host / hostport / database / username / passwordpgsql / 127.0.0.1 / 5432 / — / postgres / —
行为-drop-target-first迁移前清空目标库全部表(慎用)
-tables只迁指定表(逗号分隔白名单,留空迁全部)
-batch-size数据搬运批大小(默认 1000)

核心设计 ​

text
独立于业务基座:
  gdb.New 动态连接源库与目标库(独立命名节点 migrate_src/migrate_dst,
  不触碰 config.yaml 默认连接),裸读裸写——
  全程不触发全局钩子(租户隔离/软删/审计盖章),数据原样落库

五方言目标渲染器(Renderer 接口:Quote/Column/CreateTable/CreateIndex/
  DropAllTables/FixSequence):
  mysql   AUTO_INCREMENT + COMMENT 内联
  pgsql   IDENTITY + COMMENT ON 独立语句 + setval 序列修复 + CASCADE
  sqlite  极简类型 + AUTOINCREMENT + 免修序
  mssql   IDENTITY + NVARCHAR + 扩展属性注释 + 内联 INSERT(绕 2100 参数上限)
          + DBCC CHECKIDENT 修序
  oracle  IDENTITY + NUMBER/NVARCHAR2 + COMMENT ON + 免修序

中间表示(IR):20 种归一类型;剥离 unsigned/pgsql 时区修饰;
  tinyint(1)→bool;索引命名统一 uk_/idx_ 规范(迁移后全库一种命名)

数据搬运 doCopy:主键游标分页;无主键表直接报错("请先补主键")

数据搬运循环图(doCopy,主键游标分页) ​

text
lastPK=nil
   │
   ▼
┌──────────────────────────────────────────┐
│ SELECT 列集 FROM src.table                │
│   WHERE pk > lastPK ORDER BY pk LIMIT N  │◀──────┐
└──────────────┬───────────────────────────┘       │
      行数=0?─ 是 ─▶ 返回 total                    │
               否                                  │
               ▼                                   │
batch=[{列:值(字节转字符串)}…],lastPK=末行 pk ────┘
               │
               ▼
目标写入:通用方言 → 参数化多行 INSERT
          mssql   → 内联字面量 INSERT(自增列:事务内 IDENTITY_INSERT ON/OFF)
               │
               ▼
total += len(batch);onBatch 进度回调;len(batch)<N → 结束

要点:源端按主键排序快照分页(快照语义,并发写入可能漂移);字节切片值转字符串(convertValue);mssql 因 2100 参数占位符上限走内联字面量 + 单批小事务(IDENTITY_INSERT ON→INSERT→OFF→COMMIT)。

关键原则与限制 ​

限制清单

  1. 无外键/视图/存储过程/触发器迁移(0 外键设计,只迁表/列/二级索引/注释/数据)
  2. 无主键表拒绝搬运(分页搬运依赖主键排序)
  3. mssql/oracle 源端索引内省未实现(作为源库时二级索引会丢)
  4. 逐表串行搬运(无并发),超大库耗时线性增长
  5. -drop-target-first 清空目标库全部表(含非本项目表),慎用
  6. 逐表行数校验不一致 → 整体失败退出码 1

与五方言定稿脚本的关系 ​

text
document/{mysql,postgresql,sqlserver,oracle,sqlite}/easygoadmin.goframe.elevue.sql
  = "定稿 DDL"(mysql 为规范基准,人工导入维护)

迁移器 = "运行期动态等价物"(自动跟随源库结构)

两者独立演进:新表/新索引需两边同步(DDL 脚本手工维护)

实测记录 ​

仓库保留实测日志(可参考排障):

text
document/数据迁移PostgreSQL版本日志.txt
document/数据迁移SqlServer版本日志.txt
document/数据迁移Sqlite版本日志.txt
document/数据迁移命令.txt(完整参数说明与示例)

首期实测链路 mysql→pgsql;mssql/oracle 按定稿方言约定实现,待人工校对。

使用建议 ​

text
1. 迁移前备份源库与目标库
2. 首次迁移用 -tables 白名单先迁小表验证连通与方言转换
3. 全量迁移加 -drop-target-first(确认目标库无他用!)
4. 观察每表行数校验 PASS/FAIL;FAIL 表排查后 -tables 单独重迁
5. 迁移后核对索引(uk_* 命名已统一)与注释(非 MySQL 目标语义可能降级)
6. 应用侧切库:改 config.yaml database.default.type/link 重启

小蚂蚁云团队 · 提供技术支持