☰
Oracle到MySQL数据同步实战:DataX并发通道与分片配置详解
2026/9/26 8:42:56 网站建设 项目流程

在一家同时跑着 Oracle 和 MySQL 的公司里做数据平台,最常遇到的场景就是:核心业务系统都在 Oracle 上,但后建的 BI 报表、经营分析、数据中台只认 MySQL。两边要定期同步数据,这活儿听起来简单,真做起来却有一堆讲究。

我最开始是让开发写存储过程,通过 dblink 往 MySQL 插数。结果每次同步都心惊胆战:源库 CPU 被拉高,目标库写入一慢就堵事务,不同数据库之间的类型还得手工转换,字段一多配置就乱。后来换了 DataX,再把同步任务统一放到 DataX-Web 上调度管理,才算是把这块彻底理顺。这篇就完整记录一次 Oracle 到 MySQL 的实战配置。重点不是教你怎么点按钮,而是把 DataX 里最核心的四个概念——并发通道(Channel)、分片字段(splitPk)、TaskGroup(任务组)、Task(子任务)——用一条真实同步配置串起来讲明白。适合刚开始接触 DataX、想把批量同步任务做得又快又稳的读者参考。

1. 先说清楚业务场景:Oracle 主库为什么非得往 MySQL 同步

很多公司并不是一开始就想搞两套数据库,而是业务发展过程中自然长成的。我们这边的情况是:订单、客户、商品这些核心数据都在 Oracle 里,ERP、CRM、WMS 各种系统都依赖它,不能随便动。但近几年上的数据分析平台、可视化报表、甚至是一些外包团队开发的新系统,都是基于 MySQL 的。新系统不可能直连 Oracle 生产库,只能靠周期性同步拿数据。

1.1 手工脚本同步的痛点在哪里

最早我们用的是 dblink 加存储过程,每天晚上跑批。这种方式在小数据量下没什么问题,但数据量一旦上来,问题就非常明显。

一个是数据库负载不可控。dblink 方式本质上是把源库当成了一个普通客户端,存储过程里如果写了全表扫描逻辑,Oracle 的 CPU 和 IO 会瞬间飙高,影响白天还在跑的业务。另一个是数据一致性不好保证。跑批过程中网络抖动、目标表锁冲突、字段超长,任何一个环节出错,整个存储过程就中断了,第二天发现报表数据是残缺的,还得人工对账重跑。

更麻烦的是类型转换。Oracle 的 NUMBER、VARCHAR2、DATE 转到 MySQL 的 int、varchar、datetime,不是简单的一一对应。一开始我们靠开发在 SQL 里手工写 TO_CHAR、TO_NUMBER,字段少还能应付,字段上百个以后,维护成本高到让人崩溃。

1.2 为什么挑 DataX 而不是其他同步工具

当时也考察过其他方案,比如基于日志的 CDC 工具、商业 ETL 工具,甚至直接用 Kettle 拖拽。最后选 DataX 主要是看中几点。

DataX 是阿里开源的异构数据源同步框架,插件化设计,Oracle 和 MySQL 的 reader、writer 都是现成的,改改 JSON 就能用,不需要写一行业务代码。它对源端是普通的 JDBC 查询方式,不依赖数据库日志,也就不会对 Oracle 的归档模式、日志解析权限提出额外要求,这一点在生产库上很重要。还有一个原因是它的并发模型设计得比较直观,一个 channel 就是一条数据通道,想提速加通道数就行,出问题容易排查。

当时也担心过 DataX 社区维护节奏的问题,但这么多年用下来,发现同步任务这种场景对版本迭代并不敏感,稳定才是第一位的,所以一直用到现在。

1.3 DataX-Web 在 DataX 之上补了什么

单纯用 DataX 命令行走任务,问题在于没有统一的管理界面。任务分布在各个服务器上,跑没跑成功全靠人肉盯日志,调度只能靠 crontab,谁改过任务配置也无从追溯。

DataX-Web 是一个基于 DataX 的可视化调度管理平台。它把 DataX 的 Job JSON 封装成了任务模板,再通过任务绑定执行器的方式,把任务分发到指定机器上执行。页面里能看到每个任务的历史执行记录、日志、运行状态,还能配 cron 表达式做周期性调度。对我来说,它最大的价值不是省了敲命令那几秒钟,而是让整个同步体系从“脚本散落”变成了“平台统一”。

2. 环境准备与第一个同步任务的完整发布流程

现在开始进入实操。假设你已经有两套环境:Oracle 19c 在 192.168.10.20,MySQL 8.0 在 192.168.10.30,网络互通,内网同步。下面这套流程我按自己的部署习惯写,版本不同界面可能有细微差别,但核心思路一致。

2.1 环境安装:两台机器需要准备什么

DataX 本体和 DataX-Web 可以部署在同一台机器上,也可以分开。我建议把 DataX 装到离源库网络比较近的一台单独服务器上,别和业务应用混跑,同步任务对带宽和 IO 的占用不小。

安装 DataX 本体很简单:下载 DataX 安装包,解压后目录里有 bin、plugin、lib 等几个子目录。核心的可执行脚本在bin/datax.py。我们需要确认 Python 环境可用,且本机已经装了 JDK 8 及以上版本。DataX 本身是 Java 写的,JDK 版本太老或太新都可能踩坑。

DataX-Web 我用的是 GitHub 上开源的版本,它分为两个部分:前端 Web 页面和执行器(executor)。前端负责配置任务、查看日志;执行器负责真正拉起 DataX 进程。部署时先把 DataX-Web 的后端服务启动起来,再配置执行器地址,让前端能连接到执行器机器。

有一点容易被忽略:DataX-Web 的调度中心和执行器最好都配置了时区,否则定时任务的执行时间和服务器本地时间对不上。我们线上就出过一次任务提前一小时跑的故障,查了半天发现是容器时区默认 UTC 导致的。

2.2 DataX-Web 里创建一个同步任务的入口

在 DataX-Web 页面上,正常流程是:

  1. 先创建项目,一般一个项目对应一个业务线。
  2. 进入“任务管理”模块,创建任务模板,把 DataX 的 JSON 配置粘贴进去。
  3. 创建任务,选择刚才的模板,绑定执行器和调度表达式。
  4. 如果想立即验证数据,可以手动触发运行,先不配调度。

这里要分清楚“任务模板”和“任务”的关系。模板是静态配置,相当于一类任务的公共定义;任务才是调度实体,绑定执行器后才有实际的运行记录。同一个模板可以创建多个任务,比如不同日期的增量同步,通过任务参数区分。

2.3 一个可直接复制的 Oracle→MySQL 任务 JSON

下面是核心部分,先看一个完整的、可用的 JSON 配置。

{ "job": { "setting": { "speed": { "channel": 4, "byte": -1, "record": -1 }, "errorLimit": { "record": 0, "percentage": 0.02 } }, "content": [ { "reader": { "name": "oraclereader", "parameter": { "username": "report_ro", "password": "your_password", "connection": [ { "table": ["orders"], "jdbcUrl": ["jdbc:oracle:thin:@//192.168.10.20:1521/orcl"] } ], "column": [ "ID", "ORDER_NO", "AMOUNT", "CREATE_TIME" ], "splitPk": "ID", "where": "CREATE_TIME >= to_date('2024-08-01', 'yyyy-mm-dd')" } }, "writer": { "name": "mysqlwriter", "parameter": { "username": "report", "password": "your_password", "connection": [ { "table": ["orders"], "jdbcUrl": ["jdbc:mysql://192.168.10.30:3306/report_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true"] } ], "column": [ "ID", "ORDER_NO", "AMOUNT", "CREATE_TIME" ], "preSql": ["truncate table orders"], "writeMode": "insert", "batchSize": 2048 } } } ] } }

这个配置的含义很直白:reader 从 Oracle 的 orders 表里读 ID、ORDER_NO、AMOUNT、CREATE_TIME 四列,只取 2024-08-01 之后的数据;writer 把数据写入 MySQL 的 orders 表。写入前先清空目标表,适合全量刷新场景。

重点先看两个参数:splitPk和speed.channel。这两项直接关系到并行度,后面会专门展开。byte和record都设成 -1,表示不限流,不走字节数或记录数限制,优先保证速度。如果担心源库压力,可以给 byte 设一个值,比如 10MB/s,DataX 会按这个速度限流。

2.4 为什么 Oracle Reader 要加 where 条件

很多新手做全量同步时会在 Oracle 源端直接整表查询。表小的时候无所谓,表一大就很伤:DataX 虽然会做并发分片,但查询条件没有下推到数据库,Oracle 可能需要做全表扫描,共享池和 IO 都会被打满。

所以我习惯在 where 里先做粗过滤,把需要的日期范围或状态范围写清楚。这既能让 Oracle 走索引,也能减少网络传输的数据量。如果表有主键或者时间索引,这个 where 条件的收益非常明显。

3. 并发通道、分片字段、TaskGroup、Task 到底是怎么配合的

这是整篇的核心,也是很多人在 DataX 文档里看半天没看明白的地方。我用大白话把这四个概念拆开讲。

3.1 一个 Job 是如何被拆成多个 Task 的

DataX 里,一个同步任务整体称为一个 Job,也就是你在 DataX-Web 里配置的那份 JSON。Job 在运行时会经历两个阶段:拆分(split)阶段和执行阶段。

在拆分阶段,DataX 会调用 reader 的 split 方法,把整体的数据读取任务切成若干份,每一份就是一个 Task。Task 是最小的执行单元,它负责读取一段数据并写入目标端,内部是单线程的。如果你不配置任何分片逻辑,那 Job 只有一个 Task,全表数据由这一个 Task 串行搬运,速度自然上不去。

这个拆分动作和两个因素有关:一个是并发通道数,一个是分片字段。它们共同决定了 Task 的数量和边界。

3.2 splitPk 分片字段是怎么切数据分片的

splitPk指定一个字段,DataX 会用它把查询结果切分成多个区间。比如 orders 表的主键 ID 是从 1 到 500 万的连续自增整数,channel 配成 4,DataX 就会把数据大致均分成四个区间,例如 1~125 万、125 万~250 万、250 万~375 万、375 万~500 万,生成四个 Task。

实际执行时,每个 Task 会带着一段区间条件去查询 Oracle,相当于在 reader 的 SQL 上自动追加了类似WHERE ID >= ? AND ID < ?的条件。这样每个分片走主键或索引的范围扫描,对源库更友好,数据也能并行读取。

反过来,如果你不配置 splitPk,哪怕 channel 配了 8,DataX 也只能生成一个 Task。因为 reader 没法把数据切成多份,并发配置就形同虚设。这也是我见过最多的“配了并发没提速”的原因。

3.3 channel 并发通道控制的到底是什么

channel 中文叫通道或者管道,可以理解成一个并发线程。每个 channel 内部维护着 reader 和 writer 两端的连接,一个 channel 同时只能执行一个 Task。

你在配置里把speed.channel从 1 改成 4,本质上是告诉 DataX:同一时刻最多可以有 4 个 Task 并行执行。这 4 个 Task 会各自从 Oracle 读数据并往 MySQL 写数据。所以通道数越高,同一时刻在跑的数据库连接数、网络连接数、内存占用也会跟着升高。

3.4 TaskGroup 任务组是怎么把 Task 组织起来的

TaskGroup 是很多人最容易混淆的概念。官方文档和各类文章里一会儿说 Task 一会儿说 TaskGroup,搞得人云里雾里。

在 DataX 的运行框架里,Job 拆分出来的 N 个 Task 并不会一下子全部交给 N 个线程去跑,而是先被分到若干个 TaskGroup 中。TaskGroup 相当于一个任务容器或者调度组,组内维护着自己的 channel,负责从中取出 Task 并分发给线程执行。

你可以这样理解:Task 是“干活的单元”,channel 是“干活的人”,TaskGroup 是“干活的小组”。公司里有个大项目,被拆成了 20 个小任务,不可能 20 个人同时开工,人太多了协调不过来,所以分成了 4 个小组,每组 5 个人,每个小组认领 5 个小任务,谁干完谁去接下一个。

TaskGroup 存在的意义正是为了解决这种调度和管理问题。如果 Task 数量很大,比如几百上千个,直接全部并发会产生大量连接和线程,机器扛不住。通过 TaskGroup 分层调度,DataX 可以有节奏地把任务分配给各个通道去消费。

这里要特别提醒一下:DataX-Web 页面上也有一项叫“任务组管理”,但它和 DataX 内部的 TaskGroup 是两码事。DataX-Web 的任务组是用来给执行器分组的,比如你有两台执行器,可以分成两个任务组,分别调度不同的任务;DataX 的 TaskGroup 是运行时框架里的概念。别把这两者混了,坑过不少人。

3.5 四个概念放在同一个案例里看

还是拿上面的 orders 表举例。假设 channel=4,splitPk=ID,总数据约 500 万行。

  1. Job 是整个同步任务,负责拆解和调度。
  2. split 阶段基于 ID 把数据切成 4 份,生成 4 个 Task。
  3. 运行时框架把 Task 分配给 TaskGroup 管理,TaskGroup 里的 channel 按并发度去执行 Task。
  4. 每个 Task 负责一段 ID 区间的数据,单线程完成读取和写入。

如果在日志里看到“task 0、task 1、task 2、task 3”这样的编号,不用好奇,那就是 4 个并行 Task。再看机器上的数据库连接数,大概率也是 4 组左右的读写连接。

4. 并发参数到底怎么调:一次真实压测记录

理论讲完,必须用数据说话。下面是我环境里一次实际压测的记录,供你参考调参思路,不建议直接照搬数字,因为不同表结构、数据库硬件、网络环境差异很大。

4.1 压测环境与固定条件

源端 Oracle 19c,16 核 32G,orders 表约 500 万行,单行平均 1KB,数据总量大约 5GB。目标端 MySQL 8.0,8 核 16G。中间是千兆内网。分片字段统一用 ID 主键,writer 端 batchSize 固定 2048,不限制流量。

每个并发档位我都连续跑两次取平均值,避免第一次运行时有连接初始化等冷启动影响。表结构固定,目标表每次都先 truncate 再写入,保证口径一致。

4.2 不同 channel 数下的实测对比

结果如下表:

channel 数总耗时平均同步速度源库 CPU 峰值表现评述
118 分 30 秒约 4.6 MB/s15%稳定但太慢,单通道是保底方案
210 分 05 秒约 8.5 MB/s28%速度明显提升,源库压力可控
46 分 12 秒约 13.7 MB/s55%速度与压力比较平衡
84 分 08 秒约 20.1 MB/s80%速度最快,源库负载已偏高
165 分 40 秒约 15.2 MB/s95%速度反而下降,源库成了瓶颈

这个结果很典型:通道数从 1 加到 8,速度接近线性提升;但从 8 加到 16,不仅没有继续加速,总耗时反而变长了。原因是当 16 个并发 Task 同时在 Oracle 上做范围扫描时,数据库的 CPU、IO 都被打满,查询产生大量等待,单条 SQL 变慢,整体吞吐量反而掉下来。

4.3 怎么找到“当前环境的最优并发”

根据我的经验,最优并发不会出现在某个固定数值上,而是要综合看三条线。

第一条线是源库负载。Oracle 的 CPU 使用率尽量控制在 70% 以下,给业务留出余量。这里采集的是同步期间的峰值,如果峰值长期超过 80%,我建议降一档并发。

第二条线是目标库写入能力。MySQL 写入瓶颈往往不在 SQL 本身,而在磁盘 IO 和 binlog 落盘。可以用iostat、dstat看目标库的磁盘利用率,如果%util长期接近 100%,再往上加并发只是把压力转移到了 MySQL。

第三条线是网络带宽。千兆内网理论上有 100MB/s 左右,但实际能够稳定跑到 50MB/s 就非常不错了。如果你看到总流量已经逼近网卡上限,并发加得再多也没有意义。

所以实操时我的方法是:从 channel=2 开始跑,往上翻倍试探,每档看一次源库 CPU 峰值和总体耗时,当耗时不再下降或源库负载超过 75% 时,往回退一档,再用这一档长期跑。大多数表在 channel=4 到 channel=8 之间就能获得不错的性价比。

5. 实战最容易踩的坑:Oracle 源端和 MySQL 目标端的边界条件

配置跑通只是第一步。真正让人头疼的是那些“能跑但结果不对”或者“跑到一半报错”的情况。下面这些坑我基本都踩过,每一项都值得你在上线前检查一遍。

5.1 splitPk 选择不当导致的漏数据和数据倾斜

分片字段不是随便挑一个字段就行。最理想的是数值型主键,值单调递增、分布均匀、非空。如果选了分布不均匀的字段,比如状态字段,只有 0 和 1 两个值,DataX 切分出来的区间就只有一个能查到大量数据,其他区间几乎为空,最终还是相当于单线程跑,并发完全失效。

还有一个很容易踩的坑是分片字段存在 NULL。DataX 在做区间切分时,对 NULL 值的处理并不友好,可能出现边界条件漏数据,甚至直接报错。所以我一般建议,如果你的表在分片字段上有 NULL,要么在 SQL 里用NVL(ID, 0)处理,要么换一个非空字段。有人会说时间字段能不能作为分片字段,我的答案是能用但慎用。时间字段如果重复值多,或者时区不一致,切分后可能产生区间重叠和数据漏读。线上环境我一般只用 ID 或唯一数字序列做分片,靠谱得多。

5.2 Oracle 和 MySQL 类型映射的坑

两边数据库类型体系差异比想象中大,通常不是 DataX 本身的问题,而是建表结构没对齐。下表是我整理的核心映射参考:

Oracle 类型MySQL 推荐类型注意事项
NUMBER(18)BIGINT超过 2^31 别用 INT
NUMBER(18,2)DECIMAL(18,2)映射成 INT 会丢精度
VARCHAR2(2000)VARCHAR(2000) 或 TEXTMySQL 的 varchar 有长度上限,超了要建 text
DATEDATETIMEOracle date 带时分秒,MySQL date 不带,务必用 datetime
TIMESTAMPDATETIME注意时区传参,jdbcUrl 里加 serverTimezone
CLOBLONGTEXT大文本字段要做字符集校验
BLOBLONGBLOB二进制字段看业务是否必须同步

最容易中招的是 NUMBER 精度过大。Oracle 里NUMBER(38,0)如果直接映射到 MySQLINT,数值一大就会报Out of range value for column,整条数据写入失败。我在一次渠道订单同步中就因为这个字段导致不少订单缺失,最后靠 errorLimit 里的脏数据记录才定位到。建目标表时宁可字段宽一点,也不要刚好卡边界。

5.3 字符集、时区、批量写入的边界

Oracle 字符集是 ZHS16GBK,MySQL 可能建表时用的 utf8mb4,两边不一致时最容易出现乱码或者是“字符串截断”报错。DataX 的 JDBC URL 上要明确字符集参数,Oracle 端一般通过oracle.jdbc.defaultNChar或连接属性做控制,MySQL 端在 jdbcUrl 里加上characterEncoding=utf8mb4。记住一个原则:连接串上的字符集一定要明确写出来,别用数据库默认值。

时区问题主要在 Oracle 的TIMESTAMP WITH TIME ZONE类型。同步到 MySQL 的 datetime 字段时,如果两边时区不同,时间数据会被 JVM 默认时区影响,多出 8 个小时或者少 8 个小时。我建议在启动 DataX 的 JVM 参数里统一指定时区,比如-Duser.timezone=Asia/Shanghai,别依赖操作系统默认时区。

批量写入也有讲究。MySQL Writer 的batchSize默认值是 1024,设大一点能明显提升写入效率,但也不是越大越好。我实测 2048 到 4096 是比较合适的区间,再大容易撑爆 MySQL 的 max_allowed_packet,出现Packet for query is too large报错。批量写入还建议在 jdbcUrl 上加rewriteBatchedStatements=true,这个参数能让 JDBC 把多条 insert 合并提交,性能提升非常可观。

5.4 常见报错与排查方向汇总

遇到报错先不要急,按下面的表格对照排查:

报错特征大概率原因排查动作
ORA-12899 value too largeOracle 源字段长于 MySQL 目标字段检查两边字段长度定义,加宽目标表
Out of range value for columnNUMBER 精度映射错误目标列改成 DECIMAL 或 BIGINT
Packet for query is too largebatchSize 太大或字段内容超大调小 batchSize,检查是否有超长文本
Communications link failure网络抖动或数据库连接被断开检查网络稳定性,适当降低并发
乱码字符集不一致统一 JDBC 连接串字符集参数
同步缺失部分数据splitPk 有 NULL 或分片不均匀检查分片字段空值,换主键字段

DataX 日志里报错信息一般会带上具体插件和行号,比如oraclereader-0表示第 0 个并发分片。如果 errorLimit 配置允许了脏数据比例,DataX 会把失败记录写到日志里,但业务表数据已经缺失,所以生产环境我一般把record设成 0,宁可任务失败也不允许静默丢数。

6. 从“跑通一次”到“长期稳定同步”的运维习惯

最后一个环节是让任务活下来,而不是跑一次就完事。这部分更多是运维经验,数据同步的稳定性问题几乎都集中在调度和监控上。

6.1 DataX-Web 里的调度与执行日志

DataX-Web 支持 cron 表达式调度。我的标准配法是:全量同步放凌晨业务低峰,cron 表达式写清楚分钟和小时,比如0 2 * * *表示每天凌晨 2 点。增量同步则根据业务时效要求调整频率,有的半小时一次,有的一天一次。

任务运行后,一定要养成看执行日志的习惯。DataX 每次任务结束都会打印“任务启动时刻”、“任务结束时刻”、“任务总计耗时”,还会输出平均流量和错误记录数。如果平均流量明显低于历史水平,即使任务显示成功,我也建议去查一下,看是不是源库加了慢查询或者网络出了问题。

日志里另外一个有用信息是脏数据统计。DataX 会明确打印脏数据条数和占比,如果百分比不断上升,说明源端或目标端的数据质量出现了变化,早发现早处理。

6.2 增量同步怎么设计才稳健

全量同步用truncate + insert没问题,但对大表来说每次全量成本太高。到了后期,我更推荐用增量同步来做日常更新。

增量同步的核心思路是加一个时间水位线。在 DataX-Web 的任务参数里,每次调度时传入一个业务日期或者上次同步时间,然后在 reader 的 where 条件里用它过滤数据。

"where": "CREATE_TIME >= to_date('${lastSyncTime}', 'yyyy-mm-dd hh24:mi:ss')"

任务参数通过 DataX-Web 的运行参数传入,类似-DlastSyncTime=2024-08-01 00:00:00。上次同步时间可以查目标表里的 max(CREATE_TIME),也可以在调度外层用一个状态位维护。增量字段建议选择有索引的时间列,否则每次增量查询全表扫一遍,代价比全量还大。

增量同步最怕的是时间字段回拨或者数据补录。业务人员手工改了一条历史订单的时间,会导致这条数据不在增量窗口内。所以很多团队会保留每周一次全量作为兜底,配合每日增量,这个组合在工程上非常实用。

6.3 我在实际维护中总结的几条经验

同步任务谁都会配,但能稳定跑半年不出问题,靠的是细节。

第一,DataX 所在服务器的磁盘空间要盯紧。DataX 本身不落业务数据,但日志文件会一直增长。DataX-Web 的执行器日志如果不定期清理,几个月就能吃掉几个 GB。我在日志目录上挂了定时清理任务,只保留最近 30 天。

第二,不要把执行器跟其他重负载应用混布。同步任务吃 IO 和 CPU,和数据量大的在线服务放在一起,两边都不痛快。独立机器、独立资源是最省心的。

第三,任何配置改动都要先在测试表上跑一遍。DataX 的 JSON 配置看着简单,但一个括号写错就会导致任务解析失败,如果直接在线上改,又赶上没人在场,问题只能等到次日数据对账时才发现。

最后一个小技巧:在 DataX-Web 的任务名称上,把业务含义、同步周期、目标表名写清楚,比如“orders全量同步_每日凌晨2点_写入orders报表库”。听起来很简单,但当你维护几十个任务时,一个清晰的任务命名能节省大量排查时间。数据同步这件事本身不难,难的是把每一步都做得规范,让系统在没有人工干预的情况下也能按预期运转。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询