Oracle 到 MySQL 迁移复盘:自增主键、时间类型和空值才是真正的坑

一次 Oracle 11g 到 MySQL 8.0 的生产迁移复盘:200+ 张表、约 3 亿行数据。迁移本身两天跑完,踩坑和修复多花了一周。问题不在导数据本身,而在序列转自增、DATE 含时间被截断、Oracle 空串等于 NULL 这三个认知差。

背景

项目是从 Oracle 11g(AL32UTF8)迁移到 MySQL 8.0(utf8mb4),表结构中混用了序列自增和业务主键,时间字段全是 DATE 类型(Oracle 的 DATE 实际包含时分秒),部分旧模块仍在使用空串 '' 表示"无值"。迁移不能停服超过 4 小时,之后需要 CDC 实时追增量。

方案怎么定

迁移分两个阶段:窗口期全量,窗口后增量追平再切读。

条件选择原因
窗口期全量全量一次性覆盖全部 200+ 张表
夜间低峰追增量增量字段(UPDATE_TIME)表上没有统一的 binlog 消费链路,CDC 需要额外开日志且审批周期长
后续实时同步CDC增量字段不能捕获 DELETE,最终还是要上 CDC

最终组合:全量 + 增量字段追平 → CDC 持续同步。全量用一种工具,CDC 另起一种,不如一个工具三种模式全支持,省掉来回切配置的成本。

真正卡住的地方

整轮迁移里真正卡住时间的不是数据量大,是下面三个 Oracle 独有的行为差异。

坑一:序列转自增,主键值没跟上

Oracle 用 SEQUENCE 生成主键,很多表的自增逻辑写在触发器里。MySQL 只有 AUTO_INCREMENT,不会自动建序列对象。

坑二:Oracle DATE 迁移到 MySQL DATE 被截断时分秒

Oracle 的 DATE 类型精确到秒,MySQL 的 DATE 只有年月日。

坑三:Oracle 空串等于 NULL,MySQL 里它们是两回事

Oracle 会把 '' 自动当成 NULL 存储。MySQL 不会,'' 就是一个空字符串。

对比:这几次迁移分别踩的坑

踩坑点Oracle → MySQLMySQL → 达梦SQL Server → MySQL
自增/序列SEQUENCE 不存在,需手动设 AUTO_INCREMENT自增→IDENTITY,注释需检查identity 转 AUTO_INCREMENT,种子值需校对
时间DATE 含时分秒,需改 DATETIMEDATETIME 精度差异datetime2 精度 7 位,MySQL 只到 6 位
空值空串 = NULL 的行为差异空串处理一致空串处理一致

跟 MySQL→达梦 那次比,Oracle 迁移多了一个时间类型语义鸿沟和一个空串的认知差。跟 SQL Server→MySQL 那次比,Oracle 的序列问题又比 identity 转 AUTO_INCREMENT 难处理——identity 是列的属性,序列是独立对象,耦合在触发器和代码里。

用 DataMover 怎么落地

Oracle 端准备好只读账号,MySQL 端准备写入账号。DataMover 以 Docker 方式部署,Manager 在 8000 端口,Worker 在 8011。

  1. 配源端:新增 Oracle 数据源,填 SID/Service Name、主机端口、只读账号。连接测试 通过后再进下一步。
  2. 配目标端:新增 MySQL 数据源,填主机端口、写入账号、目标库名。
  3. 创建普通任务:选择 Oracle 为源,MySQL 为目标。全量阶段就选普通任务。
  4. 选表:从源端树结构中勾选需要迁移的表,支持单选和批量勾选。
  5. 检查字段映射:这里是整轮迁移最关键的一步。DataMover 会自动匹配同名字段,但需要手动逐表确认:
    • 所有 Oracle DATE 列的目标类型改为 DATETIME
    • 源端 NUMBER 列确认目标精度:NUMBER(*) 默认映射到 DECIMAL(38,9),纯整数字段建议收窄到 BIGINT
    • VARCHAR2(4000) 超出 MySQL 行大小限制时,考虑降为 TEXT
    • 有空串风险的非空列,在 exp 列中配置 default()
  6. 配置同步策略:全量任务选择"全量同步",设置合适的批大小(大数据量表建议 2000-5000,不要盲目调大)。目标表策略选择"覆盖"或"追加",窗口期迁移建议先清空目标表再用"追加"。
  7. 启动任务:选择执行 Worker,点击执行。Manager 下发任务,Worker 并行读取 Oracle 和写入 MySQL。
  8. 监控和日志:在执行记录中查看进度、行数和错误日志。日志里会列出所有字段类型转换警告和写入错误。

校验和返工

全量跑完后,用下面这组 SQL 做校验:

-- 1. 总行数校验
SELECT COUNT(*) FROM source_table;
SELECT COUNT(*) FROM target_table;

-- 2. 时间范围校验
SELECT MIN(create_time), MAX(create_time) FROM source_table;
SELECT MIN(create_time), MAX(create_time) FROM target_table;

-- 3. 关键字段聚合校验
SELECT status, COUNT(*) FROM source_table GROUP BY status ORDER BY status;
SELECT status, COUNT(*) FROM target_table GROUP BY status ORDER BY status;

-- 4. 抽样逐列对比
SELECT * FROM source_table WHERE id IN (100, 5000, 100000);
SELECT * FROM target_table WHERE id IN (100, 5000, 100000);

-- 5. 空串专项检查
SELECT COUNT(*) FROM source_table WHERE remark = '';
SELECT COUNT(*) FROM target_table WHERE remark = '';

校验不过的就查日志,修正字段映射后重跑该表,或者单独建一个增量任务只拉错掉的区间。

复盘结论

  1. 先验类型再跑全量:迁移前把每张表的字段类型导成 Excel,逐个标记 Oracle 类型 → 目标类型 → 人工检查项,再用自动映射的结果对照。不要靠跑完全量再发现类型错误。
  2. 序列不是建表的一部分:自动建表工具不迁移序列。提前列好序列清单,全量完成后批量 ALTER TABLE ... AUTO_INCREMENT = N
  3. Oracle DATE 在 MySQL 里永远是 DATETIME:没有例外。Oracle 没有独立的 DATETIME 类型,所有带时间的列都在 DATE 里。迁移时必须手动转。
  4. 空串问题是数据库中立的陷阱:开发在 Oracle 里习惯写 SET col = '',DBA 在 Oracle 里 COUNT(*) WHERE col IS NULL 会包括空串。到了 MySQL,这两个查询结果不一样。上线前手写查询验证。
  5. 增量字段追平只是过渡:增量字段不能捕获 DELETE,也无法保证 UPDATE 一定更新了增量字段。最终还是要上 CDC。选工具的时候先确认 CDC 支持情况,省得中间又切一套工具。

FAQ

Oracle 的 NUMBER(*) 迁移到 MySQL 用什么类型?

DataMover 默认映射到 DECIMAL(38,9)。如果该列实际只用整数值(如主键、状态码),手动改为 BIGINT。如果确实需要超过 18 位精度的大数,保持 DECIMAL 但注意 MySQL 的 DECIMAL 最大 65 位。

Oracle 到 MySQL 迁移支持 CDC 实时同步吗?

支持。DataMover 基于 Debezium 实现 Oracle CDC,需要源端开启 Archive Log 并配置 LogMiner 权限。社区版就支持 CDC,不限制数据量。但 CDC 快照策略需谨慎选择——目标表已有数据时不要选"清空目标表"。

全量迁移对 Oracle 源库性能影响多大?

DataMover 采用无锁读取方式,对源库性能影响极小。建议在业务低峰期执行全量迁移,并控制 Worker 的批大小和并发数,避免目标 MySQL 写入压力过大。

迁移完以后还需要核对什么?

除了行数和抽样对比,还必须核对:金额字段的合计值、时间字段的时区偏移、枚举字段的取值范围和计数、NULL 列的行数和比例。行数一致不表示数据一致。

下一步

Oracle → MySQL 只是去 Oracle 化的第一步。如果目标端未来还要迁到达梦、GaussDB 等国产库,核心经验可以复用——序列转自增、时间类型核对、空串检查这三件事换什么目标库都不会过时。

相关同步方案

除了Oracle到MySQL数据迁移同步完整方案,DataMover还支持以下场景:

Oracle到MySQL数据迁移Oracle迁移MySQL方案Oracle同步MySQL数据库Oracle去Oracle化迁移Oracle到MySQL增量同步Oracle CDC实时同步MySQLOracle到MySQL字段类型映射Oracle SEQUENCE转自增

常见问题解答

Oracle的DATE迁移到MySQL用什么类型?

Oracle的DATE包含时分秒,MySQL的DATE只有年月日。迁移时必须将Oracle DATE改为MySQL DATETIME或TIMESTAMP,自动建表工具不会自动区分这个差异。

Oracle的序列SEQUENCE怎么迁移到MySQL?

MySQL没有SEQUENCE对象,需使用AUTO_INCREMENT替代。迁移完成后需手动执行ALTER TABLE设置起始值,并检查依赖序列的触发器和代码逻辑。

Oracle到MySQL的CDC实时同步支持吗?

支持。DataMover基于Debezium实现Oracle CDC,需要源端开启Archive Log。社区版就支持CDC。

Oracle的空串迁移到MySQL会出问题吗?

Oracle中空字符串等同于NULL,MySQL中空字符串和NULL是不同的。有NOT NULL约束的列可能在写入时报错,需在字段映射中配置默认值或清洗源端数据。

开始配置 DataMover 同步任务

下载 DataMover 后,可按文档完成数据源、表映射、同步策略、任务启动和执行结果校验。