DataX 一条脏数据拖垮整表同步:errorLimit 的 record 0 和 percentage 0.02 先改掉
某制造企业的数仓每天凌晨用 DataX 把业务库的订单明细同步到 Hive,跑了两个多月一直正常。某天凌晨,任务跑到 99% 突然失败,调度系统重试一次还是失败,值班同学只能手动重跑整表,报表晚了两个小时才出来。
查日志,原因一句话就能说清:源表里混进了一条脏数据,金额字段里出现了一个空字符串,写目标表时类型转换失败。几十万条里就这一条有问题,可 DataX 的处理方式是:整单任务直接报错退出,一条都不放过。
一条脏数据就能让任务失败,是 errorLimit 的默认值在起作用
每个 DataX 作业的 JSON 里都有一个 setting.errorLimit,管的就是脏数据容忍度。DataX 自带的作业模板里,它是这样写的:
"setting": {
"speed": {
"channel": 3
},
"errorLimit": {
"record": 0,
"percentage": 0.02
}
}
两个值的含义,华为云官方的 DataX 最佳实践文档里写得很清楚:record 是脏数据最大记录数阈值,percentage 是脏数据占比阈值,达到任一阈值,DataX Job 就报错退出。
关键在 record 的 0。它的语义是"脏数据条数上限,超过就报错",0 条上限等于一条都不允许。任务跑到最后汇总时,只要读写失败总数大于 0,第一个条件就先触发了——percentage 的 0.02(也就是 2%)根本没机会发挥作用。几十万条数据里混进一条脏数据,失败数是 1,占比远不到 2%,但 record 这一关已经把它拦下来了。
所以这次失败不是源端数据"太脏",而是默认值定得太严:模板把容忍度写成了零容忍。很多作业直接从模板复制配置,从来没改过这两个数,平时源端数据干净就相安无事,一旦上游哪天手抖写进一条非法值,凌晨的同步就准时炸掉。
先定位:脏数据是哪一条,日志里只能看到前 10 条
改阈值之前,先把脏数据找出来。DataX 的任务汇总里有一行"读写失败总数",不为 0 就说明确实有脏数据;但汇总不告诉你是哪条。脏数据的样例只保留在日志的错误收集器里,core.json 里 maxDirtyNumber 默认是 10,也就是最多只留前 10 条。数据量一大、脏数据一多,第 11 条往后长什么样,日志里根本看不到。
定位最省事的办法,是拿 reader 的 SQL 先去源端筛一遍。下面这条 SQL 查的是最常见的两种脏法:金额字段混进非数字字符、日期字段出现非法值。例子里源表的金额字段是 VARCHAR,目标表是 DECIMAL,类型转换失败就是这么来的:
SELECT order_id, amount, order_date
FROM orders
WHERE amount NOT REGEXP '^-?[0-9]+(\\.[0-9]+)?$'
OR order_date = '0000-00-00'
LIMIT 20;
amount 为 NULL 的行不会被这条 SQL 捞出来,因为 NULL NOT REGEXP ... 的结果是 NULL 而不是真;空字符串 '' 和 N/A 这类值则会被正则筛出来。如果筛出来的是零星几条,就是偶发脏数据;如果筛出来一整批,说明上游的写入逻辑或者字段类型已经变了,处理方式完全不同。
可以直接抄用的配置:把阈值改成"允许少量脏数据,但超了就失败"
定位之后再决定阈值。下面这份作业配置给的是"每天同步几十万条订单明细、偶尔混进零星脏数据"的场景,源端是 MySQL,目标是 Hive 的 ORC 表:
{
"job": {
"setting": {
"speed": {
"channel": 3
},
"errorLimit": {
"record": 100,
"percentage": 0.001
}
},
"content": [
{
"reader": {
"name": "mysqlreader",
"parameter": {
"username": "sync_user",
"password": "sync_password",
"connection": [
{
"jdbcUrl": ["jdbc:mysql://192.168.1.10:3306/sales_db"],
"table": ["orders"]
}
],
"column": ["order_id", "amount", "order_date"],
"where": "order_date >= '2026-11-09'"
}
},
"writer": {
"name": "hdfswriter",
"parameter": {
"defaultFS": "hdfs://192.168.1.20:8020",
"fileType": "orc",
"path": "/user/hive/warehouse/sales_db.db/orders",
"fileName": "orders",
"column": [
{"name": "order_id", "type": "BIGINT"},
{"name": "amount", "type": "DECIMAL"},
{"name": "order_date", "type": "STRING"}
],
"writeMode": "append"
}
}
}
]
}
}
record 100、percentage 0.001(也就是 0.1%)这两个数是经验值,不是官方推荐:意思是每天几十万条里允许丢掉最多 100 条脏数据,或者脏数据占比不到千分之一,任务继续跑;一旦超过,任务照样失败报警。阈值放多宽,取决于下游能不能接受少量订单缺失——报表上少几单可以事后补,少几千单就是事故,这个数得按自己的业务定,不能照搬。
放宽阈值不等于放任脏数据丢了。被丢掉的脏数据不会进目标表,下游报表里这部分订单就是缺失的。所以阈值放宽后要配一条监控:每天早上看一眼任务汇总里的"读写失败总数",不为 0 就去源端查那几条脏数据是怎么来的,修源头,而不是年年放宽阈值。
另外注意两件事。第一,DataX 的退出码里 0 表示成功、-1 表示失败,调度系统可以按退出码判断要不要重试;但脏数据超限导致的失败不要做无脑重试,重试多少次结果都一样,只会重复跑全量。第二,慢和失败是两回事,别拿加 channel 治脏数据。core.json 里每个 TaskGroup 的 channel 默认是 5,单通道字节限速默认 -1(不限速),加并发解决的是速度问题,脏数据超限只能靠改 errorLimit 或者修源端数据。
放宽之前,先排掉两个真问题
阈值只能兜住零星的脏数据,兜不住成批的类型问题。改配置之前,按顺序查这两处:
第一,看是不是源端字段类型变了。 上游表结构变更是最常见的成因,比如金额字段从 DECIMAL 改成 VARCHAR,或者日期字段开始允许 0000-00-00。这种情况下脏数据会成批出现,上面那条筛查 SQL 会捞出一大片。这时调阈值是掩耳盗铃,先让上游把类型改回去,或者在 reader 的 SQL 里把非法值转换掉。
第二,看是不是 writer 的列映射写错了。 reader 的 column 顺序和 writer 的 column 顺序没对上,整列都会写错位置,脏数据跟着成片出现。先对一下两边的列顺序,再看是不是某列的类型在目标表里定义错了。
小结
DataX 一条脏数据就能拖垮整表同步,根子在 errorLimit 的模板默认值:record 是 0、percentage 是 0.02,record 为 0 意味着一条脏数据就判任务失败,percentage 根本没机会起作用。先拿 SQL 在源端定位脏数据,确认是零星偶发再把阈值改成允许少量脏数据的经验值,同时配上"读写失败总数"的日常监控。阈值兜得住零星脏数据,兜不住成批的类型问题——先排掉源端类型变更和列映射写错这两个真问题,再谈放宽。
- 点赞
- 收藏
- 关注作者
评论(0)