5.3 Reader端性能优化策略(SQL优化、索引、批量读取)


5.3 Reader端性能优化策略(SQL优化、索引、批量读取)

很多任务慢,根子在 Reader 端:源库查询没走索引、一次性拉太多、或者没分片。Reader 端的优化,本质是「让源库舒服地吐数据」。

让查询走索引

DataX 的查询最终是一条 SQL。如果 where 条件没命中索引,源库会全表扫描,Reader 自然慢,还会长时间锁表。我们写查询前必看执行计划,确认过滤列有索引。对于按时间增量的场景,created_at 上的索引是标配。

-- 在源库确认查询走了索引 EXPLAIN SELECT id, name FROM user WHERE created_at > '2024-01-01'; -- 若 type=ALL 则为全表扫描,需要给 created_at 加索引

splitPk 与批量

splitPk 把大表切成多段并发(3.1)。fetchSize 控制每次 JDBC 拉取的批大小。两者配合:分片让多任务并行,fetchSize 让单任务少往返。我们一般 splitPk 用自增主键,fetchSize 设 2000 左右,再根据内存微调。

{ "reader": { "name": "mysqlreader", "parameter": { "connection": [{ "jdbcUrl": ["jdbc:mysql://h:3306/d"], "table": ["orders"], "splitPk": "id" }], "column": ["id", "user_id", "amount"], "where": "created_at >= '2024-01-01'", "fetchSize": 2000 } } }

05-03-fig01

下推优于拉取

能用 querySql 在源库完成过滤、简单聚合的,绝不全拉到 DataX 再处理。源库有索引、有统计信息,执行效率远高于 DataX 的单线程变换。这是我们反复强调的「下推原则」在性能上的回报。

边界提醒

splitPk 列若分布不均(比如大量历史数据 id 集中在某段),分片会倾斜,有的 Task 跑很久、有的早早结束。遇到这种情况,我们换更均匀的分片键,或手动指定 split 的边界值。

索引是 Reader 的命根

DataX 的查询最终是一条 SQL,where 条件没命中索引就会全表扫描,Reader 自然慢,还会长时间锁表影响线上。我们写查询前必看执行计划,确认过滤列有索引,尤其按时间增量的场景,created_at 上的索引是标配。

检查项 不满足的后果
where 走索引 全表扫描拖慢
splitPk 均匀 分片倾斜
fetchSize 合理 往返多或内存涨

我们曾因漏建索引,一条增量任务把源库 CPU 打满,连带影响线上交易。此后所有同步查询上线前都要过索引评审。

延伸与提醒

生产环境的稳定性,常常取决于部署习惯而非某个高级特性。
Channel 是有界缓冲,填满即触发反压保护内存。
权限收得越紧,凭据泄露的爆炸半径越小。
自定义插件最容易踩的坑是依赖冲突,provided 范围能治本。
writeMode 必须和数据更新语义对齐,不能凭感觉选。
把转换逻辑下推到源库,通常比在 DataX 内部处理更高效。
一张参数与吞吐的经验曲线,比任何通用公式都贴近你的环境。
调优的本质是在 CPU、网络带宽和磁盘 IO 之间找平衡点,不是堆资源。
任务的读写速率差,比绝对速率更能说明瓶颈在哪一段。
退出码接进调度系统,才能让失败在半夜被及时发现。
任务可重跑幂等,是生产上线前的硬指标。
脏数据阈值设得太高,会掩盖源端的数据质量问题,反而埋雷。
Reader 和 Writer 互不知晓,正是插件能独立扩展的原因。
querySql 与 column 二选一,混用会直接报错。
源库索引评审应作为同步查询上线的前置环节。
JVM 堆要给 Channel 缓冲留足空间,否则 GC 频繁拖慢吞吐。
对象存储比 FTP 更适合做跨机房中转,因为它支持断点和内网加速。
监控指标和 DataX 日志交叉看,能锁定九成瓶颈。
机器核数、内存、带宽三者共同决定 channel 的甜点值。
星型拓扑把 N 乘 M 的对接降到 N 加 M,变更成本随之下降。
channel 数超过源端连接承受能力时,瓶颈会从 DataX 转移到数据库。
数据湖贴源层保留原始形态,方便后续 schema 演化。
eswriter 的批量 bulk 写入,比逐条插入快一个数量级。
Redis 同步重跑要防重复 key,靠固定模板才能幂等。
preSql 里带 truncate 的任务,上线前必须二次确认目标表名。

背景

Reader 端优化集中在「少读无用数据、并行读、批量读」。对关系型源,这几个手段往往比加 channel 更有效。

操作:SQL 优化 + 索引 + 批量读取的组合

{ "job": { "content": [ { "reader": { "name": "mysqlreader", "parameter": { "username": "etl", "password": "secret", "connection": [ { "jdbcUrl": ["jdbc:mysql://db:3306/order"], "table": ["t_order"], "splitPk": "id" } ], "column": ["id","user_id","amount","create_time"], "where": "create_time >= '2024-01-01'", "fetchSize": 2000, "querySql": "SELECT id,user_id,amount,create_time FROM t_order WHERE create_time >= '2024-01-01' AND id >= ${start} AND id < ${end}" } }, "writer": { "name": "hdfswriter", "parameter": { "defaultFS": "hdfs://ns", "path": "/data/order", "fileType": "text", "column": [ {"name":"id","type":"bigint"}, {"name":"user_id","type":"bigint"}, {"name":"amount","type":"double"}, {"name":"create_time","type":"string"} ] } } } ], "setting": { "speed": { "channel": 10 } } } }

启动命令

# 在数据库侧确认抽取 SQL 走了索引,避免全表扫把源端拖死 mysql -u etl -p demo -e "EXPLAIN SELECT id FROM t_order WHERE create_time>='2024-01-01';"

结果解读

  • 少读:column 只选必要列、where 尽早过滤,降 IO 与序列化开销;
  • 并行:splitPk 让框架按主键范围分片并行;
  • 批量:fetchSize 调大减少 JDBC 往返;
  • 索引:确保 where / splitPk 命中索引,否则单分片查询就全表扫,并发反而放大源端压力。

变式

对超大类表,可先用数据库分区裁剪(where 命中分区键),再配合 splitPk,把「扫描量」和「并发度」同时压下来。

Reader 优化对照

手段 收益 前提
精减 column 降 IO 明确所需列
where 过滤 降扫描 有索引列
splitPk 提并发 整型主键
fetchSize 降往返 内存允许

💡 关键直觉:Reader 优化的第一性原则是「让源端尽量少干活」。任何能让数据库少扫一行、少传一列的手段,都会线性反映到同步速度上。

⚠️ 常见坑:splitPk 选了无索引的列,框架的每个分片查询都全表扫,channel 越大源端越惨;务必先用 EXPLAIN 确认分片查询走索引再上并发。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U