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 / password | mysql / 127.0.0.1 / 3306 / — / root / — |
| 目标库 | -dst-type / host / hostport / database / username / password | pgsql / 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)。
关键原则与限制
限制清单
- 无外键/视图/存储过程/触发器迁移(0 外键设计,只迁表/列/二级索引/注释/数据)
- 无主键表拒绝搬运(分页搬运依赖主键排序)
- mssql/oracle 源端索引内省未实现(作为源库时二级索引会丢)
- 逐表串行搬运(无并发),超大库耗时线性增长
-drop-target-first清空目标库全部表(含非本项目表),慎用- 逐表行数校验不一致 → 整体失败退出码 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 重启