DataX 同步一张大表特别慢:channel 配到 8 也没用,先看 splitPk 和 batchSize
某制造企业每天凌晨用 DataX 把 MySQL 里的设备台账表同步到数仓,表里 800 多万行。最开始 channel 配的是 1,跑了 40 多分钟;有人把 channel 改成 8,以为能快 8 倍,结果耗时几乎没变,日志里的记录写入速度一直卡在 18 万条/秒上下。遇到这种情况,先别急着加机器、加通道,问题大概率出在两个地方:读端没切分,写端批量太小。
一、channel 再多,也超不过切出来的 task 数
DataX 的 channel 是并发通道,但它有个硬上限:实际生效的通道数不会超过读端切分出来的 task 数。JobContainer 的源码里写得很直白,needChannelNumber = Math.min(needChannelNumber, taskNumber)。也就是说,读端只切出 1 个 task,你配 8 个 channel 也只有 1 个在干活,剩下 7 个是摆设。
对 mysqlreader 来说,切分的关键就是 splitPk。官方文档写明了:如果不填 splitPk,DataX 视作使用单通道同步该表数据。而且 splitPk 只支持整型字段切分,浮点、字符串、日期都不行,配了会直接报错;推荐用表主键,因为主键分布通常比较均匀,不容易出现数据热点。
官方性能报告里的数据很能说明问题。同一张单表,不按主键切分时,channel 配 1、4、8,速度都是 183185 条/秒,一点没变;按主键切分后,4 通道 329733 条/秒,8 通道 549556 条/秒。这才是 channel 该有的样子。
所以第一步是给读端配上 splitPk:
"reader": {
"name": "mysqlreader",
"parameter": {
"username": "sync_user",
"password": "sync_password",
"column": ["id", "device_code", "device_name", "update_time"],
"splitPk": "id",
"connection": [
{
"table": ["device_info"],
"jdbcUrl": ["jdbc:mysql://192.168.1.10:3306/prod_db"]
}
]
}
}
这里 id 是 device_info 表的自增主键。配完之后再去看日志,确认 task 数是不是真的切开了——如果日志里还是 1 个 task,说明 splitPk 没生效,先别往下调别的参数。
还有一点容易被忽略:DataX 是按主键的最小值到最大值等分区间来切分的,如果主键分布本身不均匀,切出来的分片就会有大有小,最慢的那个分片会拖住整个作业。比如早期数据被批量清理过、主键中间缺了一大段,或者新老数据量悬殊,都可能出现这种情况。动手之前可以先查一下主键的最大值、最小值和总行数,心里有个数;如果分布确实很偏,就别指望只靠加 channel 解决问题,先处理数据分布,或者接受单通道跑全量、按时间范围拆成多个作业分批跑。
一个提醒:用 querySql 自定义 SQL 的作业,DataX 会忽略 table、column、where 的配置,splitPk 同样不生效。这种作业想提速,只能改 SQL 本身,比如按主键范围拆成多个作业,或者换回 table 模式。
二、写端的 batchSize 默认 1024,别只盯着 channel
读端切开了,写端也得跟上。mysqlwriter 有个 batchSize 参数,默认值是 1024,意思是每攒够 1024 条记录才批量提交一次。这个值太小,DataX 和 MySQL 之间的网络往返次数就多,吞吐上不去;但也不是越大越好,官方文档明确写了,设置过大可能造成 DataX 运行进程 OOM。
官方的写入性能报告给了一个参考刻度:batchSize 到 512 之后,单通道写入速度能到每秒 1 万行;在此基础上,通道数在 32 以内时,速度随通道数线性增长;同时官方建议写数据库时通道数通常不要超过 32。报告里还有一个反面例子:16 通道配 4096 的批量,测试中出现了 2 次 Full GC。
按这个刻度调,先给 writer 加上 batchSize:
"writer": {
"name": "mysqlwriter",
"parameter": {
"writeMode": "insert",
"username": "sync_user",
"password": "sync_password",
"column": ["id", "device_code", "device_name", "update_time"],
"batchSize": 2048,
"connection": [
{
"jdbcUrl": "jdbc:mysql://192.168.1.20:3306/dw_db",
"table": ["device_info"]
}
]
}
}
经验值:一般先从 2048 起步,跑一轮看耗时和 GC 情况;如果单行很宽(比如带大文本字段),就往 1024 靠;如果行很窄、机器内存宽裕,再往 4096 试,但每次只动一个参数,改完对比日志里的记录写入速度和 Full GC 次数,再决定留不留。
三、两个容易忽略的细节
第一,speed 必须配。channel、byte、record 三种限速方式至少要配一种,否则作业启动就报错,错误码是 CONFIG_ERROR,报错信息是 Job运行速度必须设置。这是 JobContainer 里直接抛出来的,很多新手第一次写作业就栽在这里,还以为是插件没装好。
第二,jdbcUrl 不用自己拼批量参数。DataX 会在你提供的 jdbcUrl 后面自动追加 rewriteBatchedStatements=true 等属性,配合 PreparedStatement + Batch 把攒够一个 batchSize 的记录一次性发出去。自己再拼一遍不会更快,反而容易拼错。
另外 writeMode 的默认值是 insert,主键冲突的行会写不进去;如果同步逻辑要求冲突时覆盖,改成 replace,但这会先删后插,写放大和主从延迟都要重新评估,别顺手改。
小结
DataX 同步大表慢,先按这个顺序排查:第一,看读端有没有配 splitPk,没配就是单通道,channel 配多少都没用,官方报告里不切分时 8 通道和 1 通道速度完全一样;第二,看写端 batchSize,默认 1024,可以先试 2048,超过 4096 要盯着 Full GC;第三,确认 speed 配了限速方式,channel 一般不要超过 32。把这三处改对,再去看日志里的记录写入速度,多数情况下不用加机器就能把同步时间压下来。
- 点赞
- 收藏
- 关注作者
评论(0)