做了这么多年数据库相关的活儿,跨库迁移是绕不开的一个坎。尤其是Oracle往MySQL迁,项目背景五花八门:有的是集团统建系统要下沉到本地化部署,有的是为了降成本从商业数据库切到开源栈,还有的是数据仓库前置库要换成轻量方案。我最近正好用Navicat Premium完整做了一次Oracle到MySQL的数据迁移,整个过程踩了不少坑,也总结了一套相对靠谱的流程。这篇就按实际操盘的顺序,把从选型、预检、表结构转换、数据灌入到事后校验的完整过程写出来,给同样要干这活的朋友一个参考。
整个迁移用到的核心工具就是Navicat Premium,它内置的“数据传输”功能可以直接在Oracle和MySQL之间建通道,不需要先导出成中间文件再导进来,省了很多事。不过工具只是省力,真正决定迁移质量的还是前期准备和对两个数据库差异的理解。下面逐一展开。
1. 迁移方案选型与整体设计思路
1.1 为什么用Navicat Premium而非手工或命令行
先说结论:如果是一次性的、数据量在GB级别以下、表结构不算特别变态的迁移,Navicat Premium基本是最省心的选择。
有人会问,直接用Oracle的expdp导出dmp,再用MySQL的source导入不行吗?理论上行,但实操很痛苦。dmp是Oracle私有二进制格式,MySQL根本不认,中间还得再过一层SQL解析转换。更麻烦的是,Oracle和MySQL的SQL方言差异不小,字段类型、函数、日期处理、保留字都不一样,直接灌SQL脚本大概率在第一个CREATE TABLE就报错。
命令行方案(比如写Python脚本用cx_Oracle和pymysql逐表读再写)灵活度高,但代码量大,调试成本高,而且对非开发背景的DBA不友好。我自己也写过类似的脚本,最后发现大部分时间都花在了类型映射和特殊字符清洗上,纯属重复造轮子。
Navicat Premium的“数据传输”工具把这条链路简化成了三步:配好两个连接、勾选要迁移的表、点开始。它内部会做类型映射、批量提交、错误跳过这些事,对绝大多数场景够用了。而且它支持在迁移前预览和修改字段映射关系,等于给了你一层保险,发现问题可以在启动前就调整。
1.2 迁移链路的整体架构与前置条件
这次迁移的目标库是MySQL 8.0(InnoDB引擎,utf8mb4字符集),源库是Oracle 11g R2(生产库,数据量约300G,单表最大的在1.2亿行左右)。链路是:Oracle实例 → Navicat Premium客户端 → MySQL实例,网络走的是内网专线。
这里有个关键点:Navicat Premium只是一个客户端工具,它不负责“搬运”数据文件,而是逐条把源库的数据SELECT出来,再INSERT到目标库。所以它的性能上限受两个因素制约:一是客户端机器到两个数据库的网络带宽和延迟,二是客户端本身的单线程处理能力。大表的迁移速度不会特别快,但胜在稳定可控,出错了能断点续跑(其实Navicat没有真正意义的断点续跑,但可以按条件分批续传,后面细说)。
前置条件里,最容易被忽略的是Oracle客户端的OCI库。Navicat Premium连接Oracle不是纯JDBC直连,而是通过本机的Oracle Instant Client(或者完整版Oracle Client)提供的OCI接口。机器上没装OCI的话,连接测试会直接报错。我第一次用Mac版Navicat连Oracle时就被这个坑过,后来装了对应架构的Instant Client并配好环境变量才通。
MySQL这边相对简单,官方Connector/C是Navicat自带的,基本不用额外配置。只要MySQL实例开了远程访问权限,创建一个有建库建表权限的账号就行。
2. 迁移前的预检与准备:这些事没做,后面全是坑
2.1 源库与目标库的权限、版本、字符集核对
迁移前一定要先做一轮体检,别拿到库就开始导。我一般按这个顺序检查:
- 版本确认:Oracle 11g和MySQL 8.0之间的类型映射是最成熟的,如果是Oracle 19c或者MySQL 5.7/5.6,个别映射会有差异,需要提前看Navicat的映射规则。
- 权限确认:Oracle侧需要能读取数据字典视图(DBA_TABLES、DBA_TAB_COLUMNS等)的权限,实际迁移时至少要有SELECT ANY TABLE的权限,否则非当前Schema的表虽然能看到名字,但查询时直接报ORA-00942。如果表是别人Schema下的,还需要GRANT SELECT权限。MySQL侧需要有CREATE、ALTER、INSERT、DELETE、INDEX权限,如果走数据传输里的“删除目标表”选项,最好再给个DROP权限。
- 字符集:数据库迁移最怕乱码。Oracle库查一下NLS_CHARACTERSET,如果是ZHS16GBK,而MySQL目标库是utf8mb4,中文和生僻字(比如GBK里没有的emoji)都有可能出问题。稳妥的做法是MySQL侧建库时直接指定utf8mb4,Navicat会在迁移时做字符集转换。
- 目标库预处理:在MySQL里先手工建好目标数据库,并指定字符集和排序规则。我习惯用:
CREATE DATABASE target_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;注意:排序规则别用utf8mb4_general_ci,虽然它排序快一些,但对中文和特殊字符的处理不如utf8mb4_unicode_ci严谨。如果后续要做全文索引,utf8mb4_unicode_ci也兼容性更好。
2.2 表清单梳理与大表、无主键表标记
拿到源库后,建议先跑一遍元数据查询,把要迁移的表清单拉出来。Navicat里的“数据传输”能自动列出所有表,但手工做一张Excel清单更利于把控进度,尤其是在表数量上百的时候。
我的做法是查Oracle的数据字典,把表名、行数估算值(NUM_ROWS)、是否分区、是否有主键、最大字段类型都记下来:
SELECT table_name, num_rows, partitioned FROM all_tables WHERE owner = 'SOURCE_SCHEMA' ORDER BY num_rows DESC;这张清单的核心作用是帮你提前识别两类风险表:
一是大表(行数超过千万)。这类表在Navicat传输时建议单独勾选,不跟小表混在一起跑,否则中途某个小表报错会导致整个批次失败回滚,浪费时间。
二是无主键或无唯一索引的表。MySQL的复制、binlog、甚至后续的增量同步都依赖主键,如果源表没主键,迁移过去后建议在MySQL侧补一个自增ID,不然后续做数据比对、增量更新时会非常痛苦。
2.3 磁盘空间与网络带宽预估
这是一个经常被忽视的环节。Oracle数据文件300G,不代表你MySQL目标库只需要300G。MySQL的InnoDB引擎表数据和索引存储在一起,但碎片率、列格式(DYNAMIC还是COMPACT)、字符集转换后的字节数增长,都会让实际占用比源库更大。中文从ZHS16GBK(2字节)转成utf8mb4(3字节),光这一项数据体积就可能膨胀50%。
另外,Navicat做数据传输时,客户端所在机器会充当数据中转站,不需要额外的磁盘空间存储中间文件(它走的是内存流),但如果是用“转储SQL文件”再导入的方式,那中间SQL文件的大小就非常可观了,甚至可能达到源库大小的1.5到2倍。所以能用“数据传输”直连就不要先导出再导入。
网络带宽上,千兆内网环境下,Navicat的传输速度大概能跑到50MB/s到100MB/s,300G的数据按这个速度算,纯数据迁移时间大约在1到1.5小时之间。但如果客户端和数据库之间跨公网或者带宽有限,传输时间会成倍增加,这种情况建议先做一次小表试迁移来估算速度。
3. 表结构迁移与数据类型映射:Oracle和MySQL的“翻译官”
3.1 Navicat自动迁移的默认映射规则
Navicat的数据传输工具在勾选“创建表”选项后,会读取源表的DDL,然后按内置规则翻译成MySQL的CREATE TABLE语句。默认映射大致如下:
| Oracle类型 | MySQL类型 | 说明 |
|---|---|---|
| VARCHAR2(n) | VARCHAR(n) | 字符语义一致,直接映射 |
| NVARCHAR2(n) | VARCHAR(n) | 注意字符集,NVARCHAR2是Unicode语义 |
| NUMBER(p, s) | DECIMAL(p, s) | p>18时会映射成DECIMAL,注意精度 |
| NUMBER(1) | TINYINT(1) | MySQL里TINYINT(1)经常被当布尔用,业务侧要注意 |
| NUMBER(10) | INT / INTEGER | 只要p在INT范围内就映射成INT |
| NUMBER(19) | BIGINT | 19位就是BIGINT上限 |
| DATE | DATETIME | Oracle的DATE含时分秒,MySQL的DATE只有日期,必须用DATETIME |
| TIMESTAMP | TIMESTAMP | 但默认值和时区处理逻辑不同 |
| CLOB | LONGTEXT | 4GB上限,一般够用 |
| BLOB | LONGBLOB | 二进制大对象直接对位 |
| RAW(n) | VARBINARY(n) | 字节流,注意长度单位 |
| FLOAT(b) | FLOAT / DOUBLE | 二进制精度可能变化 |
| LONG | LONGTEXT | Oracle的LONG是历史遗留类型,映射没问题但有兼容性风险 |
| ROWID | 不支持 | 需要手工去掉,ROWID没有实际业务意义 |
3.2 默认映射的“坑”与手工修正
默认映射看着挺美好,但实际跑出来往往有几个地方要手工调整。
第一个坑:NUMBER(p, s)带s的情况。Oracle里NUMBER(10, 2)表示总长度10位、小数位2位,MySQL映射成DECIMAL(10, 2)没问题。但Oracle允许NUMBER(38),也就是最大38位精度的数字,MySQL的DECIMAL最高只支持65位,但Navicat可能直接映射成DECIMAL(38, 0),这就把小数位丢了。如果源表里有NUMBER(38, 6)这种定义,一定要在映射界面手动改成DECIMAL(38, 6),否则数据精度会丢,而且不会报错——这是最危险的静默错误。
第二个坑:Oracle的DATE类型。这个我特别想强调,Oracle的DATE实际上是一个包含时分秒的日期时间类型,用法上等同于MySQL的DATETIME。Navicat默认把它映射成DATETIME,这个是对的。怕就怕有人手工改成DATE,迁移后所有的时间字段都变成00:00:00,业务直接崩。我自己遇到过这个问题,排查了半天才发现是目标表结构被手工改过。
第三个坑:CLOB字段的迁移方式。Oracle的CLOB在Navicat里默认映射成LONGTEXT,单值大小上限4GB,一般不会超出。但CLOB的读取和写入走的是LOB定位器,Navicat在批量提交时如果遇到超大CLOB(几百MB级别的XML或JSON),可能会把整个事务撑爆。所以含CLOB的大表,建议在传输选项里把“每次批处理的行数”调小一些,比如从默认的1000降到100,避免单批数据体积过大。
3.3 索引、约束和自增序列怎么处理
Navicat在创建目标表时,会连带创建主键和普通索引。但有两个东西它默认不做,需要你手工补:
一是Oracle的SEQUENCE转MySQL的AUTO_INCREMENT。Oracle表的主键通常来自序列(比如SEQ_USER_ID.NEXTVAL),迁移后MySQL表结构里不会有AUTO_INCREMENT属性。处理方法是:在MySQL目标表上手工修改主键列为自增:
ALTER TABLE user_info MODIFY COLUMN id BIGINT NOT NULL AUTO_INCREMENT;然后把自增起始值设置成源表当前最大ID加1:
ALTER TABLE user_info AUTO_INCREMENT = 1000001;如果不做这一步,业务新增数据时主键就可能冲突,特别是应用代码里还残留着Oracle的序列调用逻辑的话(比如直接用SELECT SEQ.NEXTVAL FROM DUAL去取新ID),那在MySQL里会直接报错,等于逼着业务侧改代码。
二是Oracle的触发器(Trigger)。如果你迁移的库里有大量用于维护审计字段、复杂默认值的触发器,它们不会跟着数据迁移过来。Navicat的“数据传输”不迁移存储过程、函数、触发器这些PL/SQL对象,它只处理表结构和表数据。这意味着迁移完以后,所有依赖数据库端逻辑的字段(比如自动写入CREATED_DATE、更新LAST_UPDATED_DATE等)都会失效。这个必须提前告知业务方,让他们在应用层补齐逻辑,或者在MySQL里用新语法重建触发器。MySQL的触发器语法跟Oracle差异很大,不能直接复制SQL,需要重写。
三是Oracle的虚拟列和物化视图。虚拟列(Virtual Column)在Navicat的默认映射里可能会被当成普通列迁移,但它的生成表达式是Oracle语法,MySQL不认识,结果就是建表时报错。物化视图就更不用说了,MySQL没有原生物化视图,需要改成普通表+定时刷新的方案。这些都属于迁移范围之外但迁移后必须处理的存量问题,最好在项目计划里单独列出来。
4. 核心实操:Navicat Premium数据迁移全流程
4.1 第一步:配置Oracle与MySQL连接
在Navicat里,“连接”面板分别创建Oracle连接和MySQL连接。
Oracle连接的关键配置项有:
- 主机:Oracle所在服务器IP
- 端口:默认1521
- 服务名或SID:这里最容易弄混。Oracle 11g默认用SID,但很多云数据库对外暴露的是Service Name(服务名)。填错了会报ORA-12514或ORA-12505。建议直接问DBA要tnsnames.ora里的NET_SERVICE_NAME。
- 连接测试:如果提示找不到OCI库(类似“Cannot load OCI DLL”),就要去Oracle官网下载对应版本的Instant Client,解压后把路径填到Navicat的“OCI library”配置项里。
MySQL连接的配置相对简单:
- 主机、端口(默认3306)、用户名、密码,填入即可。
- 连接测试通过后,建议右键连接,打开“编辑连接”,在“高级”标签里确认“使用MySqldump工具”和“使用mysql工具”的路径是否正确。这两个路径用于后续的备份还原,虽然数据传输模式下用不到,但提前配好没坏处。
4.2 第二步:使用“数据传输”工具进行表结构+数据全量迁移
具体操作路径是:在Navicat主界面,选中源Oracle连接下的某个Schema(或者直接右键数据库名),点菜单栏的“工具”→“数据传输”。
在弹出的窗口里,需要做以下设置:
源:选择要迁移的Oracle数据库(Schema),不要选整个实例,否则会把系统表、系统视图也带进来。
目标:选择之前建好的MySQL数据库。
传输选项:
- “创建表” :勾选,这样会自动建目标表
- “数据” :勾选,这是数据迁移的主开关
- “删除目标表”:如果目标表存在且要覆盖,就勾选;如果目标库是空的,可以不勾
- “使用事务”:建议勾选,这样单个表的数据要么全部写入,要么全部回滚,不会出现半截数据
- “每次批处理行数”:默认1000,按需调整
对象列表:这里会把源库里的所有表列出来,可以在这一步筛选需要迁移的表,也可以单独调整每个表的目标映射。
点击“开始”后,Navicat会进入传输进度界面,能看到每个表的状态、传输行数、耗时、错误信息。整个过程建议人盯着,至少在头几分钟内确认没有报错再离开。
4.3 第三步:分批迁移大表与续传技巧
一次性把所有表都勾上跑,如果中途有一张表出错,Navicat默认会继续跑后面的表,但出错的那张表不会自动重试。我之前遇到的情况是某张表的CLOB字段里有非法字符,直接中断了整张表,后面的表还是正常跑完了,但那张表始终是0行,很隐蔽。
所以,稳妥的做法是分两批:
- 第一批:迁移所有小表、配置表,数据量小,执行时间短,适合整体跑一遍发现明显的类型映射问题。
- 第二批:逐个迁移大表。中途如果一张表跑到一半报错,可以用“筛选条件”功能做续传。
Navicat的数据传输里有一个“高级”选项,可以给源表加WHERE条件。比如一张1.2亿行的表,可以先传主键ID 0到3000万的部分:
WHERE id >= 0 AND id < 30000000跑完后再把条件改成下一段,这样即使中间失败了,顶多重跑当前段,不用从头再来。这个技巧在数据量巨大、网络不稳定的场景下能救命。
4.4 第四步:使用“Run SQL File”迁移视图和存储过程
Navicat的数据传输不迁移视图(View)、存储过程(Procedure)、函数(Function)、触发器(Trigger)等对象。这部分的迁移方式通常是:
在Oracle连接里,右键数据库 → “转储SQL文件” → “仅结构”,然后把SQL文件用文本编辑器打开,删除Oracle特有语法,改成MySQL语法前缀。改动点包括:
- 把
CREATE OR REPLACE FORCE VIEW改成CREATE OR REPLACE VIEW - 把存储过程里的
IS改成AS(MySQL用AS,但其实两种都兼容,关键是参数和变量声明方式) - 把
SELECT ... FROM DUAL去掉(MySQL不需要DUAL表,虽然8.0版也支持了但正规写法不写) - 把字符串拼接符
||改成CONCAT() - 把
SYSDATE改成NOW() - 把
TO_CHAR(字段, 'YYYY-MM-DD')改成DATE_FORMAT(字段, '%Y-%m-%d') - 把
NVL(字段, 0)改成IFNULL(字段, 0) - 把
DECODE(a, b, c, d)改成CASE WHEN a = b THEN c ELSE d END
改完之后,在MySQL连接上右键目标数据库,选择“运行SQL文件”,把改好的SQL文件跑一遍,就能把视图和存储过程建出来。这个过程不能省,否则迁移后应用系统一启动就报“找不到视图”或“Procedure不存在”的错误。
注意:存储过程这类代码逻辑的转换,最好找开发同事一起过一遍,因为不只是语法差异,Oracle的PL/SQL和MySQL的存储过程在异常处理、游标、事务控制上都有差异,只做语法翻译容易遗漏业务逻辑。
4.5 第五步:验证数据完整性与统计信息更新
数据迁移完成不等于迁移结束。我习惯做三层校验:
第一层是行数比对。在Oracle查每个表的COUNT(*),在MySQL再查一遍,两张表列出来比对。但这种全表COUNT在大表上很慢,可以用MySQL的information_schema.tables里的TABLE_ROWS和Oracle的NUM_ROWS先粗比,然后对大表抽样精确COUNT。
第二层是抽样内容比对。取每条关键业务表的前100条和最后100条,手工比对几个非索引字段,确认数据内容和顺序没有错位。
第三层是运行MySQL的ANALYZE TABLE,让优化器重新统计表信息:
ANALYZE TABLE user_info;Oracle端迁移过来的表在MySQL里,统计信息是空的,如果直接跑SQL,执行计划可能完全走偏,该走索引的走了全表扫描。ANALYZE之后执行计划就正常了。
5. 常见问题与排查技巧实录
以下这些坑,都是我实际迁移中踩过的,列出来供参考。
5.1 连接Oracle时提示“Cannot load OCI DLL”
这是Navicat连Oracle最常见的问题。原因就是本机缺少Oracle Client的OCI库,或者Navicat没有找到它。
解决方式:
- 去Oracle官网下载“Instant Client”对应版本(注意选和Navicat位数一致的,64位Navicat要64位Instant Client,32位同理)。
- 解压到本地,比如
C:\oracle\instantclient_21_12。 - 在Navicat的Oracle连接配置里,下方有一个“OCI library”的输入框,直接浏览选择刚才解压目录下的
oci.dll。
如果是在Mac上,路径类似,选libclntsh.dylib。设置完成后重新测试连接即可。
5.2 迁移时字段类型报错:Unknown column type
这个报错通常出现在Oracle 12c以后的一些新类型上,比如SYS.XMLTYPE、SDO_GEOMETRY、INTERVAL DAY TO SECOND等。Navicat对新类型的映射不完善,遇到不认识的就直接报错。
解决办法:在“数据传输”的映射界面,手动把这些列的类型改成MySQL支持的等价类型,比如XMLTYPE改成LONGTEXT,SDO_GEOMETRY改成JSON或GEOMETRY(取决于业务是否要用空间计算)。如果源表里这类列数据量大,建议先只迁移结构,再单独写脚本处理数据。
5.3 Oracle的CLOB数据导到MySQL后变截断
大字段截断的问题,通常不是Navicat的限制,而是MySQL侧的max_allowed_packet参数设置太小。MySQL默认值是64M或更小,如果单条INSERT语句里包含了一个超过这个值的CLOB字段内容,导入就会失败。
解决方式:
SET GLOBAL max_allowed_packet = 1073741824; -- 1GB注意这个参数是动态的,修改后当前会话立即生效,但重启MySQL后会失效。需要在my.cnf的[mysqld]段里也加上对应配置,才能持久化生效。
另外,在Navicat传输大字段表时,建议开启“使用事务”并在“高级”选项里把“每次批处理的行数”降低,避免单条INSERT体积过大。
5.4 主键为空的表迁移后出现重复数据
这个坑不常见,但一旦遇到就很隐蔽。Oracle表本身没有主键,但业务上可能有重复数据。Navicat创建目标表时如果也照搬了无主键结构,那么数据灌入后可能有大量重复行,后续按某字段去重时就会出问题。
我的建议是:无主键表迁移完成后,主动加一个业务无关的自增主键:
ALTER TABLE your_table ADD COLUMN id BIGINT PRIMARY KEY AUTO_INCREMENT FIRST;有了这个自增主键,后续做数据修复、增量同步、甚至简单的分页查询都更安全。如果担心业务逻辑对列顺序有依赖,可以用AFTER关键字把自增列加到末尾,不改变原有列顺序。
5.5 迁移后MySQL写入慢或大批量更新卡死
数据迁过去之后,如果业务跑起来明显变慢,先别急着说MySQL不如Oracle。检查三件事:
- 是否执行了
ANALYZE TABLE,没有统计信息的表会生成极差的执行计划。 - 目标表是否没加索引,Oracle里的索引虽然迁过去了,但如果有复合索引、函数索引、位图索引,MySQL并不都支持,尤其是位图索引(MySQL 8.0之前完全没有),这类索引迁移时会直接丢失,需要业务侧重新设计。
- 大表是否没有分区。在Oracle里做了范围分区的表,到MySQL里如果直接退化成单表,查询性能会显著下降。MySQL 8.0支持RANGE、LIST、HASH、KEY分区,可以按原表的分区键重新设计分区方案,但要注意分区键必须是主键的一部分,这个限制和Oracle不同,设计时要特别注意。
6. 迁移完成后的常见补充操作
数据导完、验证通过,这一步是很多人会掉的坑:以为迁移完了就直接给业务方交接,结果业务一上线就各种报错。补充操作主要有以下几个。
补充操作一,是更新MySQL的AUTO_INCREMENT。如果你在迁移前修改了表结构加了自增主键,或者源表用了Oracle的序列,那么一定要把MySQL的AUTO_INCREMENT设置成源表最大ID+1,否则新插入的数据会主键冲突。这个前面提到了,再强调一次,因为实际工作中这个隐患不报错,直到业务写入才爆。
补充操作二,是核对MySQL的字符集排序规则。Oracle数据库里的中文排序规则和MySQL的utf8mb4_unicode_ci排序结果不完全一样,如果业务依赖ORDER BY中文排序,迁移后可能看起来怪怪的。建议在测试阶段专门跑一遍中文排序的SQL,确认排序结果是否可接受。
补充操作三,是检查时区设置。Oracle的SYSTIMESTAMP和MySQL的CURRENT_TIMESTAMP对时区的处理逻辑不同。MySQL的time_zone默认是SYSTEM,如果目标服务器时区和业务时区不一致,NOW()返回的时间可能对不上。建议在连接参数或MySQL配置文件里明确设置时区,比如:
SET GLOBAL time_zone = '+08:00';转移过程中如果涉及TIMESTAMP WITH TIME ZONE类型的字段,尤其要关注这一点。
补充操作四,是索引碎片整理。大批量INSERT进来的数据,InnoDB的B+树索引很容易产生碎片,跑一段时间后写入性能会下降。建议在迁移完成后跑一次OPTIMIZE TABLE(等价于重建表)来整理碎片。注意这个操作会锁表,要在业务低峰期做。
7. 迁移性能优化:让数据传输跑得更快
Navicat的默认传输参数偏保守,实际使用时可以针对大表做几项调整,明显提升迁移速度。
一是调整批处理行数。默认的1000行每批,对OLTP小表没问题,但对大表来说太小了,INSERT的执行频率太高,网络往返次数也多。我实测中把批处理行数调到5000,传输速度能提升30%到50%。如果目标表字段多、单行体积大,批处理行数调大后单条INSERT的体积也会增大,要确保max_allowed_packet足够。
二是关闭“每次传输前删除目标表数据”的选项。如果你已经确定目标表是干净的,没必要在传输前再删一次。这个选项开着,Navicat会先执行DELETE FROM或者DROP TABLE再重建,多一步操作,大表上特别浪费时间。
三是调整MySQL的写入相关参数。比如innodb_flush_log_at_trx_commit = 2(或0),sync_binlog = 0,能显著降低磁盘写入频率,加快大批量导入。但注意这些是牺牲可靠性换速度的临时参数,迁移结束后一定要改回来。我个人建议如果迁移窗口允许,直接用默认参数慢慢导,毕竟数据一致性比速度快更重要。
四是用多线程方案。Navicat的“数据传输”默认是单线程,也就是同一时刻只处理一张表。如果你的源库表很多,且都是小表,单线程的效率确实不高。Navicat Premium 17版本部分场景支持并行传输,但实测下来不是所有版本都开放。实在需要并行时,可以多开几个Navicat窗口,分别迁移不同批次的表,物理并行。这么做的前提是目标MySQL的写入性能和磁盘IO扛得住并发,否则反而会拖慢整体速度。
8. 迁移质量核验清单
最后提供一份自用迁移质量核验清单,每次迁移完都照着过一遍:
| 核验项 | 具体操作 | 通过标准 |
|---|---|---|
| 表数量一致性 | 源库和目标库查询表数量 | 完全一致 |
| 行数一致性 | 关键表逐表COUNT(*)比对 | 大表抽样,小表全量 |
| 字段类型正确性 | 抽查目标表所有字段类型与精度 | Number→Decimal位数为8位以上的需单独核对 |
| 大字段数据完整性 | 对CLOB/TEXT字段抽样比对长度和首尾字符 | 无截断,长度一致 |
| 字符集与乱码 | 抽查中文字段值和emoji字符 | 显示正常,无乱码 |
| 主键自增起始值 | 查询AUTO_INCREMENT值 | 大于源表最大ID |
| 索引与约束 | 比对源库和目标库索引数量 | 关键业务索引不缺失 |
| 存储过程与视图 | 运行编译检查 | 无语法错误 |
| 时间字段 | 抽样比对yyyy-MM-dd HH:mm:ss格式 | 时分秒不丢失 |
| 统计信息 | 执行ANALYZE TABLE | 完成 |
| 业务联调 | 让业务方跑一轮核心冒烟用例 | 通过 |
这套清单看起来繁琐,但实际执行下来也就半天时间。相比迁移后线上出问题再排查,这点时间花得非常值。
我自己做了这么多次迁移,最大的体感是:Navicat这个工具解决的是“把数据搬过去”的问题,但真正决定迁移成败的是“搬之前怎么想”和“搬完之后做什么”。数据类型映射、自增主键、统计信息、字符集这些细节,任何一个没处理到位,都可能让后续的业务运行埋下隐患。希望这篇内容能帮到正打算动Oracle到MySQL迁移的朋友,少走几步弯路。