DataX这个同步工具,用好了确实省心,但写作业配置的时候,十个人里有八个会掉进JSON结构和参数语义的坑里。今天咱们就用大白话把那些容易踩的地方全捋一遍,争取下次写配置一次过。
一、先别急着写JSON,想想你要干什么
DataX是个离线数据同步工具,专门用来在各种数据源之间搬数据。比如把MySQL里的业务表搬到HDFS,或者把Oracle的表同步到另一个Oracle库,再或者把日志文件导入到数据库。它把一次同步任务叫一个job,而job的说明书就是一个JSON文件。JSON里写清楚从哪读、写到哪、并发多少、出错到多少算失败等等。
应用场景很典型:数仓建设、数据迁移、定时备份。优点是门槛低,缺点嘛,配置一旦复杂起来,JSON会变得很长,而且参数名里的“单复数”和“大小写”也容易让人头大。所以咱们先把这个JSON骨架摸清楚。
1.1 DataX JSON的“三层骨架”
一个标准的作业配置,最外层是job,要求很死板。job里面必须有一个content数组,数组里装着真正的同步任务。任务对象里必须同时有reader和writer,各自又包含name和parameter。name是插件名字,parameter是插件参数。job外面还可以放一个setting,用来控制速度、并发和错误容忍度。
先看一个最小配置,感受一下层级:
// 技术栈:DataX JSON
// 注意:JSON标准不支持注释,这里为了方便讲解才写的,实际跑的时候请删掉
{
"job": {
"content": [
{
"reader": {
"name": "mysqlreader",
"parameter": {
"username": "root",
"password": "123456"
}
},
"writer": {
"name": "hdfswriter",
"parameter": {
"fileType": "text"
}
}
}
],
"setting": {
"speed": {
"channel": 3
}
}
}
}
别小看这几层,很多同学写的JSON长得像模像样,一运行就报错,原因就是层级搞错了。
二、JSON结构里最隐蔽的坑
2.1 content是数组,不是对象
这个错误我见过至少一百遍。有人把content写成了花括号包裹的对象:
// 技术栈:DataX JSON(错误示例)
{
"job": {
"content": {
"reader": {
"name": "mysqlreader"
}
}
}
}
DataX严格规定content必须是数组,因为里面可以放多个同步任务(虽然通常只放一个)。正确写法就是用方括号包起来。万一你报错说“content不是数组”,先检查这里。
2.2 connection数组里的门道
reader和writer里的连接信息,很多参数是放在connection数组下的。以mysqlreader为例,connection是一个数组,每个元素是一个连接对象。对象里有jdbcUrl、table等。但有人会把column也放进connection里,或者把username放到connection里,这些都是错的。username和password是parameter的直系子级,column也是parameter的直系子级,而jdbcUrl和table必须在connection数组里的对象中。
错误的配置就像下面这样:
// 技术栈:DataX JSON(错误示例)
"parameter": {
"username": "root",
"password": "123",
"connection": {
"jdbcUrl": "jdbc:mysql://localhost:3306/test",
"table": "user",
"column": ["id"]
}
}
这里至少有两个问题:第一,connection应该是数组,不是对象;第二,column放错了地方。正确写法如下:
// 技术栈:DataX JSON(正确示例)
"parameter": {
"username": "root",
"password": "123",
"column": ["id"],
"connection": [
{
"jdbcUrl": ["jdbc:mysql://localhost:3306/test"],
"table": ["user"]
}
]
}
注意,jdbcUrl和table还可以用字符串数组的形式,比如"jdbcUrl": ["jdbc:mysql://..."],千万别写成单个字符串,除非某些插件明确支持。具体看文档。
2.3 列名别“裸奔”
在column数组里,如果列名是关键字,比如order、desc,或者列名有空格,直接裸写会报SQL语法错误。需要你用反引号或者双引号包起来,具体看数据源。比如MySQL用反引号:
// 技术栈:DataX JSON(正确示例)
"column": [
"`order`",
"`desc`",
"user_name"
]
如果列名里有特殊字符,比如user(name)这种,最好用反引号整个包住。不同数据库的转义符不一样,别记混了。
三、参数语义避坑:以MySQL到HDFS为例
参数结构搞对了,剩下的就是理解每个参数到底是干嘛的。拿最常见的MySQL同步HDFS来说。
3.1 reader的splitPk别乱用
splitPk是mysqlreader的一个关键参数,用来指定分片字段,好让数据按多个通道并行读取。这个字段必须是一个数字类型,而且最好分布均匀。如果表里没有合适的字段,就建议不要配置splitPk,让它单通道读,否则会调度失败。有一些人把主键id直接写上,但id并不严格递增(比如删除过记录),也可能导致分片严重不均匀。
示例:
// 技术栈:DataX JSON(推荐配置)
{
"job": {
"content": [
{
"reader": {
"name": "mysqlreader",
"parameter": {
"username": "root",
"password": "123456",
"column": [
"id",
"user_name",
"age"
],
"splitPk": "id",
"connection": [
{
"table": [
"test.user"
],
"jdbcUrl": [
"jdbc:mysql://10.0.0.1:3306/test?useSSL=false"
]
}
]
}
},
"writer": {
"name": "hdfswriter",
"parameter": {
"defaultFS": "hdfs://10.0.0.2:9000",
"fileType": "text",
"path": "/datahouse/user",
"fileName": "user_data_",
"writeMode": "append",
"fieldDelimiter": "\t",
"column": [
{
"name": "id",
"type": "int"
},
{
"name": "user_name",
"type": "string"
},
{
"name": "age",
"type": "int"
}
]
}
}
}
],
"setting": {
"speed": {
"channel": 4
},
"errorLimit": {
"record": 10,
"percentage": 0.01
}
}
}
}
注意看这个配置。column在parameter里,不在connection里。writer的column是数组对象,每个对象必须有name和type。type只能用DataX自己的类型,比如int、string、double、boolean、date。如果你写varchar,它会直接说不认识。
3.2 hdfswriter的writeMode,一个非常刺激的参数
writeMode有两种:truncate和append。truncate的意思是:同步之前,把path指定的目录先删了,再新建一个,然后写数据。如果你把path写成了某个重要目录的根路径,那跑一次,目录里的旧数据全没了。append则是直接追加。生产环境慎用truncate。
还有,fileName不是最终文件名,而是前缀。DataX会为每个channel生成一个以这个前缀开头、后面跟编号和解散符的文件。如果你把fileName写成固定的比如user.csv,两个并发通道就会互相抢同一个文件,最后可能报错或者数据错乱。
3.3 HDFS路径别带结尾斜杠
path参数别写成/data/user/这种带斜杠的,DataX有时候会对路径做拼接,容易得到双斜杠或者歪掉的路径。统一写成不带结尾斜杠的。如果目录不存在,DataX不会自动创建多层,至少在某些插件版本里不会。最好提前用HDFS命令建好目录。
四、更典型的场景:读MySQL写MySQL
除了写HDFS,最常见的还有MySQL到MySQL。这种场景下,坑主要集中在主键和写入模式上。
4.1 完整示例
先看配置:
// 技术栈:DataX JSON(读MySQL写MySQL)
{
"job": {
"content": [
{
"reader": {
"name": "mysqlreader",
"parameter": {
"username": "sync",
"password": "sync123",
"column": [
"id",
"name",
"email",
"create_time"
],
"splitPk": "id",
"connection": [
{
"table": [
"source.user"
],
"jdbcUrl": [
"jdbc:mysql://192.168.1.10:3306/source?useSSL=false"
]
}
]
}
},
"writer": {
"name": "mysqlwriter",
"parameter": {
"username": "sync",
"password": "sync123",
"writeMode": "replace",
"column": [
"id",
"name",
"email",
"create_time"
],
"connection": [
{
"table": [
"target.user"
],
"jdbcUrl": "jdbc:mysql://192.168.1.20:3306/target?useSSL=false"
}
]
}
}
}
],
"setting": {
"speed": {
"channel": 2
},
"errorLimit": {
"record": 0,
"percentage": 0.01
}
}
}
}
这里面几个细节要注意。
writer的jdbcUrl写成了字符串,而不是数组,这也是可以的,部分插件允许。但为了统一,建议也写成数组,因为有些版本解析字符串会报类型错误。
4.2 writeMode的语义陷阱
writeMode支持insert和replace。insert就是纯插入,如果目标表有主键冲突,任务直接报错,触发的重试还会插重复数据(如果没主键)。replace是MySQL语法,遇到唯一键冲突时,先删除冲突行,再插入新行。很多人以为replace是“覆盖更新”,实际上它更像是“删了再插”。所以如果你的目标表有外键关联,replace可能会因为有行被删除导致外键约束失败。
另外,如果目标表没有主键,replace就会变成普通insert,白折腾。要想真正实现“按业务键更新”,DataX自带的mysqlwriter做不到,你只能通过preSql先执行一次update,但性能很差,或者换自研插件。
4.3 preSql不是万能药
preSql是在同步之前执行的一批SQL。很多人喜欢用它来清空旧数据,比如delete from target。但要注意,多个channel并发时,preSql可能被执行多次,如果你的SQL是drop table,第二个通道执行就直接报错。最好把破坏性操作放在DataX外面,用调度系统先跑清理脚本,再启动DataX。
五、关联技术:写配置前的自检习惯
配置写完了,别急着提交。这里有几个方法和工具,能帮你少踩很多坑。
5.1 先做JSON语法检查
最简单的,把.json文件拖进VS Code,如果语法有问题会直接标红。或者用命令行工具,但那就是另外的技术栈了,这里不提。总之,确保JSON本身合法是第一道关卡。很多诡异报错,其实就是末尾多了个逗号。
5.2 用DataX的“试跑”功能
DataX有一个-dry参数,可以只建立连接、检查配置,不真正读写数据。具体运行方式你可以在网上查,我这里不敲命令,因为一敲就串技术栈了。重点是这个功能真的有用,能提前发现数据源连不上、表不存在、字段配错等问题。正式同步前,先dry跑一遍,心中不慌。
5.3 日志里藏着真正的线索
如果同步中途失败,别只盯着控制台第一行。DataX的日志会告诉你“第几行配置有问题”或者“哪个字段类型不认识”。而且报错信息里经常包含SQL语句,你可以把SQL复制到数据库客户端里自己跑一下,看看是不是参数名拼错了。
六、DataX的优点、缺点和注意事项
6.1 优点
第一,插件丰富。常见的MySQL、SQLServer、PostgreSQL、HDFS、OSS、HBase都有对应插件,生态比较成熟。第二,配置简单,比写复杂的MapReduce或者Spark程序要省事得多。第三,并发控制还算灵活,可以通过channel数量调整同步速度。第四,自带脏数据容忍机制,可以设置最多容忍多少条错误记录,避免一点点小毛病就让整个任务挂掉。
6.2 缺点
第一,JSON配置维护成本高。同步几十张表,每张表一个JSON文件,写起来像在做数据录入。第二,实时性差,它天生是离线批量工具,不适合做秒级同步。第三,ETL能力弱,它主要是“搬数据”,不是“算数据”,复杂的转换逻辑要么写SQL,要么写UDF,但UDF支持有限。第四,资源占用不好控制,通道开多了,内存和连接数会直线上升,弄不好就把源库压垮。
6.3 注意事项
生产环境使用,有几个铁律要记住。
第一,不要用root用户跑任务,权限太大,万一配置里把目录删错,后悔都来不及。第二,数据库密码不要明文写在配置文件里,尤其当配置文件放在版本仓库中时,至少要用环境变量替换,或者使用配置中心。第三,errorLimit里的参数要设置合理,record和percentage是“或”的关系,设置太大,任务会在失败的海洋里游泳。第四,大表同步时,splitPk一定要选择高区分度的数字列,比如自增主键,否则会出现数据倾斜。第五,同步完一定要做数据校验,不能只看了DataX的“写入成功”就算完。
七、文章总结
写DataX作业配置,说到底就两件事:搞清楚JSON的层级骨架,搞明白每个参数的实际语义。掉进坑里不可怕,怕的是只改一个标点就开始狂试。建议你先拿最小配置跑通,再一点点加参数,每加一个就验证一次。这样即便出了问题,也知道是刚加的那刀出的血。
最后记住一句话:DataX的文档并不算太友好,但它的报错逻辑还算忠实,很多时候不是它乱报,而是我们的参数确实没写对。把今天说的这些点挨个检查一遍,你的作业配置至少能顺滑一大半。
评论
围绕“写不好DataX作业配置,从JSON结构到参数语义全面避坑指南”参与讨论