简介:migration 是瀚高数据库官方推出的数据库迁移工具,面向需要将 MySQL、Oracle 等异构数据库平滑迁移至瀚高数据库(HGDB)的 DBA、后端开发与信创改造项目人员。工具以图形化界面引导完成迁移全流程:新建迁移组、分别建立源库与目标库连接、创建迁移任务、勾选待迁表、调整字段类型映射(如将 datetime 改为 TIMESTAMP),随后右键启动迁移并实时查看进度与日志,出错时可通过红色错误数字定位详情,降低跨库迁移门槛。压缩包为 rar 格式,整体约 247.77MB,包内以工具程序与配套说明文档为主,解压后即可通过帮助菜单查阅使用手册。目前已有 1831 人学习下载,适合正在做数据库国产化替换、需要可复用迁移流程与排错参考的技术人员参考使用。
1. 异构数据库迁移到瀚高:为什么“能跑通”和“敢上线”是两回事
做过一次 MySQL 到瀚高数据库迁移的工程师,大概都经历过这种落差:用工具把表结构和数据导过去,十分钟跑完,看着行数对得上,心里觉得这事成了。结果业务一侧连上来,第一个带GROUP BY的报表就报错,第二个带自增主键的插入就冲突,第三个存储过程直接找不到函数。数据在,逻辑不在,这就是异构迁移最典型的翻车现场。
migration 这类工具要解决的,正是从 MySQL、Oracle 这些源库把结构、数据、约束、部分逻辑对象搬到瀚高数据库的过程。它适合两类人:一类是手上真有迁移任务、需要评估工作量和风险的后端或 DBA;另一类是正在做国产化适配、想把迁移流程固化成可重复脚本的工程师。这篇笔记不讲空泛的“迁移很重要”,而是把选型、参数、脚本和踩坑按能复现的顺序摊开,让你看完能自己搭一条最小迁移链路。
2. 迁移前先想清楚:源库和目标库到底差在哪
异构迁移的难度从来不在“搬数据”,而在“翻译语义”。MySQL 和 Oracle 各自有一套默认行为,瀚高数据库虽然兼容度做得不错,但默认配置下并不会替你把所有差异抹平。先把差异摸清楚,后面写映射规则才不会边跑边改。
2.1 三类必须先对齐的语义差异
第一类是数据类型。MySQL 的TINYINT(1)常被当布尔用,Oracle 的NUMBER不带精度时行为也和瀚高的NUMERIC不完全一致。迁移工具通常会给一张默认映射表,但默认表只保证“能建出来”,不保证“业务读出来一样”。比如 MySQL 的DATETIME默认不带时区,Oracle 的DATE精确到秒,瀚高这边如果统一落到TIMESTAMP,就要确认源端有没有隐式时区转换。
第二类是自增与序列。MySQL 用AUTO_INCREMENT,Oracle 用SEQUENCE+ 触发器,瀚高更接近 Oracle 的序列思路。工具一般会把 MySQL 自增列转成序列加默认值,但序列的起始值和步长如果没对齐,批量插入后主键就会撞。
第三类是标识符大小写。MySQL 在部分系统上表名大小写不敏感,Oracle 默认大写,瀚高默认把未加引号的标识符折叠成小写。源库里如果有UserOrder这种混合大小写表名,迁移后可能变成userorder,应用里带引号的 SQL 就会找不到表。
2.2 选型:什么时候用工具,什么时候必须手写
常见做法是分三层判断。结构简单、没有存储过程、没有触发器的库,直接用 migration 这类工具全量搬,效率最高。有存储过程、自定义函数、复杂视图的库,工具只能搬个壳,逻辑体必须人工重写或改写。数据量大、还要求不停机的库,工具的全量加增量模式才值得上,否则停机窗口内做全量反而更稳。
我一般会先跑一次“只迁结构不迁数据”的干跑,把 DDL 差异全部列出来,再决定哪些对象交给工具、哪些对象手写。这个顺序能省掉大量返工,因为结构问题在空库上暴露得最快。
2.3 用一条最小命令验证连通性和权限
正式迁移前,先用工具自带的连接测试或一条简单查询确认源库和目标库都能连、账号权限够。下面这条是常见的连通性验证思路,具体命令名以你手上工具的 CLI 为准:
# 测试源库连通性,确认能读到元数据 migration-cli test-conn \ --source-type mysql \ --host 10.0.0.11 --port 3306 \ --user mig_user --password '***' \ --database app_db # 测试目标库连通性,确认有建表和数据写入权限 migration-cli test-conn \ --source-type highgo \ --host 10.0.0.12 --port 5866 \ --user mig_user --password '***' \ --database app_db逻辑说明:test-conn只做握手和权限探测,不写任何业务数据。参数里--source-type决定工具加载哪套元数据读取驱动,--database要填真实库名,别用默认库测试,否则权限判断会失真。如果这一步就报权限错误,先让 DBA 补CREATE、USAGE和序列相关权限,别急着往下走。
3. 把 MySQL 迁到瀚高:结构转换与数据搬运的完整链路
这一章按“先结构、再数据、后校验”的顺序走。每一步都给出可抄的配置和命令,参数含义写清楚,失败时看哪里也标出来。
3.1 结构迁移:类型映射表怎么改
工具一般会生成一份默认映射配置,常见格式是 YAML 或 JSON。下面是一段结构映射配置示例,重点看type_mapping和identifier_case两段:
# migration-config.yaml source: type: mysql host: 10.0.0.11 port: 3306 database: app_db user: mig_user target: type: highgo host: 10.0.0.12 port: 5866 database: app_db user: mig_user schema: public options: # 标识符统一转小写,避免应用侧带引号查询失败 identifier_case: lower # 遇到不支持的类型时中止,而不是静默跳过 on_unsupported_type: fail # 批量提交行数,过大容易撑爆目标库内存 batch_size: 2000 type_mapping: - source: tinyint target: smallint - source: datetime target: timestamp - source: double target: double precision - source: longtext target: text逻辑说明:identifier_case: lower是异构迁移里最容易被忽略的一项,设成lower后所有未加引号的标识符统一小写,应用侧只要不硬写大写引号名就能对上。on_unsupported_type: fail建议保持fail,静默跳过会让目标库缺列,后面数据插入才报错,排查成本更高。batch_size从 2000 起步,目标库内存紧张就降到 500。
参数说明:type_mapping是覆盖默认映射的入口,只写你要改的项即可,没写的走工具默认。schema在瀚高里对应模式名,多租户场景要按业务分模式,别全塞public。
3.2 数据迁移:全量与增量的取舍
结构建好后,数据搬运分全量和增量。全量适合停机窗口,增量适合要求少停机的场景。下面这条是全量迁移命令:
# 全量迁移,先结构后数据,失败即停 migration-cli run \ --config migration-config.yaml \ --mode full \ --include-schema true \ --include-data true \ --log-level info \ --error-file ./mig_error.log逻辑说明:--mode full表示一次性搬完,--include-schema和--include-data分开控制,方便你先只迁结构做验证。--error-file会把失败的行单独落盘,迁移结束后必须看这个文件,不能只看退出码。
参数说明:--log-level info够用,排查阶段可以临时开debug,但日志量会涨得很快。如果源库有外键,建议先迁数据再补外键,或者让工具在数据阶段禁用外键检查,迁完再启用,否则插入顺序会卡住。
3.3 迁移后校验:行数、抽样和约束三件套
迁移完成不等于数据正确。校验至少做三件事:行数比对、抽样字段比对、约束和索引比对。行数比对可以用下面这段 SQL,在源库和目标库各跑一次:
-- 源库 MySQL:统计每张表的行数 SELECT table_name, table_rows FROM information_schema.tables WHERE table_schema = 'app_db' ORDER BY table_name; -- 目标库瀚高:统计每张表的实际行数 SELECT relname AS table_name, n_live_tup AS row_count FROM pg_stat_user_tables ORDER BY relname;逻辑说明:MySQL 的table_rows是估算值,InnoDB 下会有偏差,所以行数比对只能当粗筛,不能当结论。瀚高这边pg_stat_user_tables的n_live_tup也是统计信息,精确值要用COUNT(*)。抽样比对挑业务主表,按主键取前 100 行和随机 100 行,逐字段比对,重点看时间字段和金额字段。
约束和索引比对容易被跳过,但恰恰是上线后性能问题的来源。迁移后要确认主键、唯一索引、普通索引都建了,外键如果业务不依赖可以不加,但要在文档里写清楚。
4. 从 Oracle 迁到瀚高:那些 MySQL 迁移不会遇到的坑
Oracle 迁移的复杂度普遍高于 MySQL,因为 Oracle 的隐式行为更多。这一章挑三个最常翻车的点讲透。
4.1 NUMBER 与 DATE 的精度陷阱
Oracle 的NUMBER不带精度时,可以存任意精度数值,瀚高的NUMERIC不带精度时行为接近,但工具默认可能映射成NUMERIC(38,10)之类带精度的类型,导致超出精度的小数被截断。迁移前要把源库里所有NUMBER列的data_precision和data_scale查出来,逐列确认映射。
DATE更麻烦。Oracle 的DATE包含年月日时分秒,没有小数秒;TIMESTAMP才有小数秒。如果源库用DATE存业务时间,映射到瀚高的TIMESTAMP(0)最稳,映射成默认TIMESTAMP会多出小数位,应用侧格式化时可能多出.000。
4.2 序列与触发器的重建顺序
Oracle 里自增常靠序列加触发器实现,迁移到瀚高后,序列要单独建,触发器逻辑要重写。顺序必须是:先建表、再建序列、再建触发器、最后迁数据。如果先迁数据再建序列,序列当前值不会自动跟上已有数据的最大值,后续插入必然撞主键。
下面这段是重建序列并同步当前值的思路:
-- 在瀚高中创建序列,起始值先设为 1 CREATE SEQUENCE seq_order_id START WITH 1 INCREMENT BY 1; -- 数据迁完后,把序列当前值对齐到表内最大主键 SELECT setval('seq_order_id', (SELECT COALESCE(MAX(id), 1) FROM orders));逻辑说明:setval的第二个参数是序列下一个返回值的前一个值,所以传MAX(id)后,下一次nextval会返回MAX(id)+1,不会撞已有主键。如果表是空表,COALESCE兜底成 1,避免setval传 NULL 报错。
4.3 存储过程改写:从 PL/SQL 到 PL/pgSQL 的映射
Oracle 的 PL/SQL 和瀚高的 PL/pgSQL 语法相近但不等价。常见差异包括:%TYPE和%ROWTYPE支持程度不同、异常处理块写法不同、EXECUTE IMMEDIATE对应EXECUTE、游标写法有差异。工具通常不迁移存储过程逻辑,只迁壳,所以这部分必须人工过一遍。
我一般会先把存储过程按“纯查询”“含 DML”“含动态 SQL”分三类,纯查询类改写最快,含动态 SQL 的最费时间,要逐条测。改写完不要只看编译通过,要用真实参数跑一遍,比对结果集。
5. 迁移避坑清单:五条血泪经验
这一章按“现象 → 原因 → 解决”写,每条都是实际迁移里高频出现的。
现象一:迁移日志显示成功,但目标库表数量比源库少。原因通常是源库有表名带特殊字符或保留字,工具默认跳过或改名失败,而日志级别不够没打出来。解决是把日志开到debug,搜skip和rename关键字,把跳过的表单独处理,保留字表名统一加双引号重建。
现象二:数据行数对得上,但金额字段小数位变了。原因是源库DECIMAL或NUMBER的精度映射到了目标库更窄的类型,超出部分被四舍五入。解决是迁移前导出源库所有数值列的精度定义,和映射配置逐列核对,宁可把目标类型放宽,也不要收窄。
现象三:迁移后应用查询变慢,执行计划全表扫描。原因是索引没迁过来,或者迁过来了但统计信息没更新。解决是迁移后对目标库执行一次统计信息收集,并核对索引清单,缺失的索引补建。
现象四:增量迁移阶段数据重复。原因是增量捕获的位点没对齐,或者源库在迁移期间有事务提交但位点记录滞后。解决是增量开始前先记录一个明确的位点,迁移期间源库尽量只读,迁完做一次全量比对去重。
现象五:中文乱码。原因是源库和目标库字符集不一致,工具连接串没指定字符集。解决是源库连接串显式指定utf8mb4或对应字符集,目标库建库时确认字符集,迁移后抽样检查中文字段。
提示:迁移期间源库如果有写入,全量和增量之间会有一段时间窗口,窗口内的变更必须靠增量补齐,否则数据会缺。停机迁移能绕开这个问题,但窗口要留够。
6. 把迁移做成可重复流程:校验脚本与回滚预案
迁移做一次靠人盯,做十次就得靠脚本。这一章讲两个具体技巧:一个自动校验脚本,一个回滚预案。
6.1 用 Python 写一个行数与抽样校验脚本
下面这段脚本做两件事:比对源库和目标库每张表的行数,对差异超过阈值的表输出警告。连接部分用占位,实际替换成你的驱动。
# migrate_check.py import pymysql import psycopg2 # 行数差异容忍阈值,超过就告警 ROW_DIFF_THRESHOLD = 0 def get_mysql_counts(conn): cur = conn.cursor() cur.execute(""" SELECT table_name FROM information_schema.tables WHERE table_schema = 'app_db' """) tables = [r[0] for r in cur.fetchall()] counts = {} for t in tables: cur.execute(f"SELECT COUNT(*) FROM `{t}`") counts[t] = cur.fetchone()[0] return counts def get_highgo_counts(conn): cur = conn.cursor() cur.execute(""" SELECT tablename FROM pg_tables WHERE schemaname = 'public' """) tables = [r[0] for r in cur.fetchall()] counts = {} for t in tables: cur.execute(f'SELECT COUNT(*) FROM "{t}"') counts[t] = cur.fetchone()[0] return counts def compare(src, dst): for table, src_count in src.items(): dst_count = dst.get(table) if dst_count is None: print(f"[缺失] 目标库没有表 {table}") continue diff = abs(src_count - dst_count) if diff > ROW_DIFF_THRESHOLD: print(f"[差异] {table}: 源 {src_count} / 目标 {dst_count}") if __name__ == "__main__": mysql_conn = pymysql.connect(host="10.0.0.11", user="mig_user", password="***", database="app_db") highgo_conn = psycopg2.connect(host="10.0.0.12", port=5866, user="mig_user", password="***", dbname="app_db") compare(get_mysql_counts(mysql_conn), get_highgo_counts(highgo_conn))逻辑说明:脚本先分别拉取源库和目标库的表清单和精确行数,再逐表比对。ROW_DIFF_THRESHOLD设 0 表示不允许任何差异,实际迁移中如果源库有持续写入,可以设一个小阈值避免误报。表名在 MySQL 里用反引号包裹,在瀚高里用双引号包裹,这是两个库的标识符引用差异,脚本里已经区分。
参数说明:连接参数按实际环境替换,密码不要硬编码在脚本里,用环境变量或配置文件读取。脚本只做行数比对,字段级抽样比对可以在此基础上扩展,按主键取前 N 行做逐字段 diff。
6.2 回滚预案:迁移前必须留的三样东西
回滚不是失败才想的事,是迁移前就要准备好的。我一般会留三样:源库迁移前的完整备份、目标库迁移前的空库快照、一份记录迁移步骤和参数的文档。备份不用多解释,空库快照是为了迁移失败时能快速清空重来,文档是为了别人接手时知道每一步做了什么。
回滚触发条件要提前定好,比如“校验差异超过 0.1% 且无法在窗口内修复”就回滚。回滚动作要演练一次,确认备份能恢复、应用能切回源库。很多团队迁移前不演练回滚,真出问题时才发现备份恢复要几个小时,窗口根本不够。
6.3 一个具体技巧:先迁一张核心表跑通全链路
最后分享一个我常用的技巧:不要一上来就迁全库,先挑一张业务核心、字段类型齐全、有索引有外键的表,单独走一遍结构迁移、数据迁移、校验、应用连接测试。这张表跑通了,说明工具配置、类型映射、字符集、权限都没问题,再放大到全库,风险可控得多。
我自己的习惯是,每次迁移前先建一个 checklist,把“连通性测试、结构干跑、单表全链路、全量迁移、校验、回滚演练”六项列上,每完成一项打个勾。这个习惯帮我避过好几次“以为没问题”的翻车。迁移这事,慢就是快,希望帮到你。
本文还有配套的精品资源,点击获取