简介:本资源为瀚高数据库迁移工具 migration 的安装包,面向需要将 MySQL、Oracle 等异构数据库平滑迁移至瀚高数据库(HGDB)的 DBA、后端开发与数据运维人员。工具以图形化界面引导完成迁移全流程,涵盖新建迁移组、配置源库与目标库连接、创建迁移任务、选择库表、匹配数据类型等环节,并针对 datetime 类型默认映射为 TIMESTAMPTZ 的问题给出改为 TIMESTAMP 的处理思路,迁移过程支持进度展示与日志回溯,出错时可点击红色错误数字查看详情,便于快速定位问题。压缩包为 rar 格式,整体约 247.77MB,内含工具程序与配套说明文档,解压后即可通过帮助菜单查阅使用手册。目前已有 1829 人学习下载,适合希望降低跨库迁移成本、减少手工改表风险的数据库从业者参考使用。
1. 迁移到瀚高:为什么异构数据库搬迁总在“最后一公里”翻车
做过国产化替换的同行大多有类似经历:MySQL 或 Oracle 上的业务跑得好好的,领导一句“迁到瀚高数据库”,测试环境一切正常,上线当晚却卡在某个存储过程、某个自增主键、某个日期函数上。瀚高数据库迁移工具(migration)要解决的正是这段“最后一公里”——它不是简单导数据,而是把异构数据库的对象结构、数据类型、SQL 方言、数据本身一起搬到瀚高数据库上。适合谁用?正在做信创替换的 DBA、后端工程师,以及需要把 mysql、oracle 存量系统平滑切到瀚高数据库的团队。这篇笔记按“先搞懂它怎么工作、再动手跑通、最后避开血泪坑”的顺序讲,参数和命令都能直接抄。
2. 迁移工具到底在搬什么:对象、数据与方言的三层拆解
很多人以为迁移就是mysqldump加source,真到异构场景才发现完全不是一回事。瀚高数据库迁移工具的核心价值,是把源库(mysql、oracle 等)和目标库(瀚高数据库)之间的差异,在三个层次上分别抹平。理解这三层,后面配置参数时才知道每个选项在动哪块。
2.1 对象层:表、索引、约束、存储过程的映射规则
对象层是迁移的骨架。源库里一张表不只是列和类型,还挂着主键、唯一约束、外键、索引、触发器、注释。迁移工具会先连源库读取元数据,再按内置映射表翻译成瀚高数据库的 DDL。以 mysql 为例,AUTO_INCREMENT要映射成瀚高的序列或SERIAL语义,TINYINT(1)常被当作布尔处理,ENGINE=InnoDB这类存储引擎子句直接丢弃。oracle 侧更复杂:NUMBER(p,s)要按精度决定落到NUMERIC还是INTEGER,VARCHAR2映射到VARCHAR,CLOB/BLOB对应大对象类型。
存储过程和函数是对象层最容易翻车的地方。mysql 的DELIMITER语法、oracle 的PL/SQL包体,都不可能原样搬过去。常见做法是工具先做语法转换,把能自动改的改掉,改不动的标记出来人工介入。我一般会先跑一遍“仅结构迁移”,把生成的 DDL 导出成脚本逐条 review,而不是直接连数据一起灌——结构错了,数据灌进去也是白灌。
2.2 数据层:批量读取、类型转换与字符集处理
数据层负责把行搬过去。工具通常用 JDBC 分批读取源库,按配置的批大小(batch size)攒够一批就往目标库写。这里有两个关键参数:批大小和提交频率。批太小,网络往返多、慢;批太大,内存吃紧、失败重试代价高。经验值是从 1000 行起步,观察内存和耗时再调。
类型转换是数据层的暗礁。mysql 的DATETIME默认不带时区,oracle 的DATE精确到秒,瀚高数据库的TIMESTAMP语义更接近标准 SQL。如果源库存了0000-00-00这种 mysql 特有的“零日期”,直接迁会报错,需要在工具里开启零日期转换或提前清洗。字符集同样要盯:源库是utf8mb4、目标库建库时用了GBK,中文和 emoji 就会变问号。迁移前务必确认目标库字符集与源库一致或兼容。
2.3 方言层:函数、分页、自增与隐式转换的差异
方言层是最隐蔽的一层,也是“测试通过、上线爆炸”的元凶。举几个高频差异:
- 分页:mysql 用
LIMIT offset, size,oracle 老版本用ROWNUM嵌套,瀚高数据库支持标准LIMIT ... OFFSET ...。工具能改写一部分,但写在字符串拼接里的动态 SQL 它无能为力。 - 自增取值:mysql 用
LAST_INSERT_ID(),oracle 用sequence.CURRVAL,瀚高数据库用RETURNING或序列。应用层代码不改,光迁库没用。 - 隐式转换:mysql 对
'123' = 123很宽容,瀚高数据库更严格,类型不匹配直接报错。oracle 里''等于NULL,这个习惯带到瀚高会出逻辑错误。
所以迁移工具能自动化的尽量自动化,但方言层一定要留人工核对清单。下面这张表是我常用的差异对照,配置映射时可以对着看:
| 差异点 | mysql 写法 | oracle 写法 | 瀚高数据库写法 |
|---|---|---|---|
| 分页 | LIMIT 10,20 | ROWNUM嵌套 | LIMIT 20 OFFSET 10 |
| 取当前时间 | NOW() | SYSDATE | CURRENT_TIMESTAMP |
| 字符串拼接 | CONCAT(a,b) | `a | |
| 空串与 NULL | 区分 | ''即NULL | 区分 |
| 自增主键 | AUTO_INCREMENT | 序列+触发器 | 序列或SERIAL |
提示:方言层不要指望工具 100% 自动改写,尤其是应用代码里的 SQL。迁移前先做一次全量 SQL 扫描,把
LIMIT、ROWNUM、NVL、IFNULL这些关键词捞出来,列成改造清单。
3. 从 mysql 迁到瀚高:一次可复现的最小迁移流程
这一章给一条能照着跑通的最小路径。假设源库是 mysql 5.7,目标库是瀚高数据库,工具以命令行/配置驱动的方式工作(不同版本界面不同,但核心步骤一致)。先把环境、连接、结构、数据四步走完,再谈调优。
3.1 环境准备与连接配置
先确认三件事:源库能连、目标库能连、工具所在机器两边网络都通。mysql 侧建议单独开一个只读账号,避免迁移过程中误写源库。瀚高侧用建库账号,提前把目标库和 schema 建好。
# 源库连通性自测(mysql 客户端) mysql -h 192.168.1.10 -P 3306 -u mig_readonly -p -e "SELECT VERSION();" # 目标库连通性自测(瀚高自带 psql 客户端) psql -h 192.168.1.20 -p 5866 -U highgo -d target_db -c "SELECT version();"连接配置一般写在一个 properties 或 yaml 文件里,核心字段就几个:源库 URL、用户名、密码、目标库 URL、用户名、密码、schema 映射。mysql 的 JDBC URL 记得带上useSSL=false&serverTimezone=Asia/Shanghai,否则时区错乱会让时间字段整体偏移 8 小时,这个坑我踩过不止一次。
# migration.properties 关键片段 source.url=jdbc:mysql://192.168.1.10:3306/biz_db?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8 source.user=mig_readonly source.password=****** target.url=jdbc:highgo://192.168.1.20:5866/target_db target.user=highgo target.password=****** target.schema=public参数说明:serverTimezone必须显式指定,不写会按 JVM 默认时区解析;characterEncoding=utf8保证中文不乱码;target.schema决定对象落到哪个 schema,瀚高默认是public,生产环境建议按业务分 schema。
3.2 结构迁移:先只迁 DDL,人工过一遍
结构迁移单独跑,不要和数据混在一起。工具通常提供“仅结构”模式,跑完会生成一份 DDL 脚本或直接在目标库建好对象。我的习惯是先生成脚本不执行,用文本对比工具逐表看。
# 仅迁移表结构,不迁数据 ./migration.sh --config migration.properties --mode schema-only --output ./ddl_out # 查看生成的建表语句,重点看类型和约束 grep -n "CREATE TABLE" ./ddl_out/*.sql | head -20逻辑说明:--mode schema-only让工具只读元数据、只写 DDL;--output把脚本落盘便于 review。重点检查三类问题:一是主键和自增有没有正确转成序列;二是TEXT/BLOB有没有被错误截断成短类型;三是外键依赖顺序,瀚高建表时如果被引用表还没建,外键会失败,需要工具按依赖排序或手动调整。
3.3 数据迁移:批大小、并发与断点续传
结构确认无误后再灌数据。数据迁移的核心参数是批大小、并发线程数和是否开启断点续传。大表(千万行以上)建议开并发,但并发数不要超过目标库的写入承受能力,一般 4 到 8 个线程起步。
# 全量数据迁移,批大小 2000,4 并发,开启断点续传 ./migration.sh --config migration.properties \ --mode>-- 源库和目标库分别执行,比对行数 SELECT 'orders' AS tbl, COUNT(*) FROM orders UNION ALL SELECT 'users', COUNT(*) FROM users; -- 目标库侧校验主键唯一性和非空约束是否生效 SELECT COUNT(*) FROM ( SELECT id FROM orders GROUP BY id HAVING COUNT(*) > 1 ) t;逻辑说明:第一条 SQL 在两边分别跑,把结果贴到一起比对;第二条检查迁移后有没有产生重复主键——批量写入时如果断点续传的分片边界没对齐,偶发重复是可能的。抽样比对可以按主键取模抽 1% 的行,把关键字段做哈希后比对,比全量比对省时间。约束验证别漏了外键,瀚高默认会校验外键,如果源库有脏数据(子表引用了不存在的父表行),迁移时会被拦下,这时要么清洗源数据,要么临时禁用外键再补校验。
4. 从 oracle 迁到瀚高:PL/SQL、序列与分页的硬骨头
oracle 迁移比 mysql 更重,因为 PL/SQL 生态深、对象依赖复杂。这一章聚焦三个最费时间的点:存储过程改造、序列与触发器、分页与函数替换。
4.1 存储过程与包的改造策略
oracle 的存储过程、函数、包(PACKAGE)迁到瀚高数据库,工具能做语法级转换,但业务逻辑必须人工过。常见转换点:NVL(a,b)改成COALESCE(a,b);SYSDATE改成CURRENT_TIMESTAMP;ROWNUM分页改成LIMIT/OFFSET;EXCEPTION WHEN OTHERS的异常处理块语法要按瀚高的 PL/pgSQL 调整。
-- oracle 原写法 CREATE OR REPLACE PROCEDURE upd_status(p_id IN NUMBER) IS BEGIN UPDATE orders SET status = 1 WHERE id = p_id; COMMIT; EXCEPTION WHEN OTHERS THEN ROLLBACK; END;-- 瀚高数据库改写后 CREATE OR REPLACE PROCEDURE upd_status(p_id INTEGER) AS $$ BEGIN UPDATE orders SET status = 1 WHERE id = p_id; COMMIT; EXCEPTION WHEN OTHERS THEN ROLLBACK; END; $$ LANGUAGE plpgsql;逻辑说明:瀚高的过程体用$$ ... $$包裹并声明LANGUAGE plpgsql;参数类型NUMBER换成INTEGER;异常块结构基本保留但要去掉 oracle 特有的包级语法。包(PACKAGE)在瀚高里没有直接对应物,通常拆成多个独立函数,或者用 schema 加命名前缀模拟。改造完一定要用真实参数跑一遍,别只看编译通过。
4.2 序列、触发器和自增主键的对应关系
oracle 12c 之前没有自增列,靠“序列 + 触发器”实现。迁到瀚高数据库时,如果目标表用了SERIAL或IDENTITY,应用层还在手动取sequence.NEXTVAL就会冲突。处理原则:要么统一改成瀚高的自增机制并改应用代码,要么保留显式序列、把触发器逻辑搬过去。
-- 在瀚高数据库创建序列并绑定默认值 CREATE SEQUENCE seq_orders_id START 1 INCREMENT 1; ALTER TABLE orders ALTER COLUMN id SET DEFAULT nextval('seq_orders_id'); -- 把序列当前值对齐到源库最大值,避免主键冲突 SELECT setval('seq_orders_id', (SELECT MAX(id) FROM orders));参数说明:setval这步千万别漏,否则新插入的数据会从 1 开始,直接撞上已有主键。触发器如果源库逻辑复杂(比如审计、级联更新),建议在瀚高侧用触发器函数重写,而不是硬翻译 oracle 的:NEW/:OLD语法——虽然概念相似,但细节差异多,硬翻容易出玄学问题。
4.3 分页、日期函数与隐式转换的替换清单
oracle 应用里ROWNUM分页和TRUNC(SYSDATE)几乎无处不在。迁移时按清单批量替换,比逐个 debug 高效。
| oracle 用法 | 瀚高数据库替换 | 注意点 |
|---|---|---|
ROWNUM <= 10 | LIMIT 10 | 嵌套子查询分页要整体改写 |
TRUNC(SYSDATE) | CURRENT_DATE | 截断到天,语义一致 |
TO_CHAR(d,'YYYY-MM-DD') | TO_CHAR(d,'YYYY-MM-DD') | 格式串基本兼容 |
NVL(a,b) | COALESCE(a,b) | 多参数场景更通用 |
DECODE(a,1,'x',2,'y') | CASE WHEN | 工具可自动转,人工复核 |
'' IS NULL | '' IS NOT NULL | oracle 空串即 NULL,瀚高区分 |
注意:oracle 里
''和NULL等价,很多业务代码靠这个特性判断“空”。迁到瀚高后''是合法空字符串,IS NULL判断会失效,导致逻辑分支走错。这类问题测试很难覆盖,必须靠代码扫描提前发现。
5. 迁移避坑实录:五条踩过的坑和排查路径
这一章全是血泪经验,每条按“现象 → 原因 → 解决”写,遇到类似报错可以直接对号入座。
5.1 中文变问号或乱码
现象:迁移完成后查询目标库,中文显示为???或乱码,英文数字正常。原因:源库字符集是utf8mb4,目标库建库时用了GBK或SQL_ASCII,或者 JDBC 连接串没指定characterEncoding。解决:迁移前用SHOW CREATE DATABASE确认源库字符集,目标库建库时显式指定ENCODING 'UTF8';连接串补上characterEncoding=utf8;已经迁错的表只能清空重迁,字符集转换不可逆。
5.2 日期时间整体偏移 8 小时
现象:源库2024-01-01 10:00:00迁到目标库变成2024-01-01 18:00:00。原因:mysql JDBC 驱动默认按 JVM 时区解析DATETIME,JVM 时区是 UTC 而数据是东八区,或者连接串没写serverTimezone。解决:连接串加serverTimezone=Asia/Shanghai,同时确认工具所在机器date命令输出的时区正确。已经迁偏的数据,按偏移量批量UPDATE回补,但要注意边界日期。
5.3 大表迁移中途失败,重跑从头开始
现象:一张两千万行的表迁到 80% 报连接超时,重跑又从第一行开始,耗时翻倍。原因:没开断点续传,或者批大小太大导致单次事务超时。解决:开启--resume true,把批大小从 5000 降到 1000 到 2000,并在目标库侧适当调大statement_timeout。断点续传依赖日志里的分片记录,所以日志目录不能删。
5.4 主键冲突:Duplicate key value violates unique constraint
现象:数据迁移报主键重复,但源库明明没有重复主键。原因:目标表用了序列自增,序列当前值没对齐源库最大值,新数据从 1 开始插,撞上已迁入的历史数据;或者断点续传的分片边界重叠导致同一批数据写了两次。解决:迁移前执行setval对齐序列;断点续传场景确认分片按主键区间切分且不重叠;已冲突的数据按主键去重后重灌。
5.5 存储过程编译通过但运行报错
现象:过程在目标库能创建成功,一调用就报“column does not exist”或类型不匹配。原因:过程体里引用的表名、列名大小写敏感问题,或者 oracle 的隐式类型转换在瀚高不成立。解决:瀚高默认把未加引号的标识符转小写,oracle 习惯大写,建表时如果用了带引号的大写列名就会对不上;统一命名规范,别用带引号的混合大小写。类型问题逐个参数核对,必要时显式CAST。
6. 让迁移可重复:把校验脚本和回滚预案固化下来
迁移不是一次性动作,国产化替换往往要经历“测试库迁一遍、预生产迁一遍、生产再迁一遍”。真正省时间的做法,是把校验和回滚做成脚本,每次迁移后自动跑,而不是靠人肉比对。
先说校验脚本。我一般写一个 Python 脚本,读配置里的表清单,对每张表做三件事:行数比对、按主键抽样的字段哈希比对、约束检查。抽样比例按表大小动态定,小表全比,大表抽 1%。哈希比对的关键是把 NULL 和空串统一处理,否则两边语义不同会误报。
import hashlib def row_hash(row, cols): # 把每行按列顺序拼成字符串再哈希,NULL 统一成固定标记 parts = [] for c in cols: v = row.get(c) parts.append('__NULL__' if v is None else str(v).strip()) return hashlib.md5('|'.join(parts).encode('utf-8')).hexdigest() def compare_table(src_conn, tgt_conn, table, cols, sample_mod=100): # 按主键取模抽样,两边用同样的取模规则才能对上 src_rows = fetch(src_conn, table, cols, sample_mod) tgt_rows = fetch(tgt_conn, table, cols, sample_mod) src_map = {r['id']: row_hash(r, cols) for r in src_rows} tgt_map = {r['id']: row_hash(r, cols) for r in tgt_rows} diff = [k for k in src_map if src_map.get(k) != tgt_map.get(k)] return diff逻辑说明:row_hash把一行数据归一化后哈希,NULL 用固定标记避免和字符串'None'混淆;compare_table按主键取模抽样,两边规则一致才能对齐。返回的diff是主键列表,拿这些主键去两边查原始行,就能定位差异。参数sample_mod控制抽样密度,100 表示抽 1%,生产校验可以调到 1000 甚至全量。
再说回滚预案。迁移前必须确认两件事:源库在迁移期间是否还在写入,目标库能否清空重来。如果源库是停写迁移,回滚就是切回源库连接;如果源库不停写,迁移期间的新增数据要单独补,回滚成本高得多。我的习惯是迁移窗口内让应用只读,迁完校验通过再放开写,这样回滚只需切连接串,不用补数据。
最后一条经验:别在周五晚上做生产迁移。听着像玩笑,但迁移这种高压力操作,出问题时能拉到的支援、能等的窗口,工作日白天和周末完全不是一个量级。把迁移当成一次有预案的发布来做,脚本、校验、回滚、值守都到位,比任何工具参数调优都管用。希望帮到你。
本文还有配套的精品资源,点击获取