ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

DataX数据同步实战:从MySQL到HDFS的配置解析与性能调优

DataX数据同步实战:从MySQL到HDFS的配置解析与性能调优 1. 内容整体设计与思路拆解1.1 为什么选择 DataX 而不是其他同步工具我一直在做数据平台的活儿日常打交道最多的就是各种数据迁移、离线同步、仓库存取。最开始那几年同步数据基本靠写Shell脚本加SQL导出导入或者用Sqoop这种老牌工具。但用着用着就发现问题了脚本一多维护成本直线上升跑挂了没人发现数据类型稍微复杂一点就开始闹脾气而且不同数据源之间的同步方式完全不统一每接一个新源就要重写一套逻辑。后来接触到DataX最大的感受就是这玩意儿把“数据同步”这件事抽象得特别干净。它不需要你部署服务端就是一个独立运行的进程你给它一个JSON格式的作业配置它就能把数据从源头拉到目的地。不像有些同步工具要装Agent、要搭管理端DataX本身就是个“跑完即走”的命令行工具特别适合那种不想引入太重基础设施的场景。我选择DataX还有几个很现实的原因第一它支持的数据源特别全MySQL、Oracle、SQL Server、HDFS、Hive、HBase、MongoDB、Elasticsearch、ClickHouse这些常见的都覆盖到了而且还在持续更新。第二它的运行机制是“读取-缓冲-写入”三段式理论上任意两个数据源之间都能通过配置组合实现同步。第三它是纯Java写的跨平台跑没问题扔到服务器上就能用。如果你只是偶尔同步几张表或者需要在不稳定的网络环境下做断点重跑DataX这种轻量级工具其实比调度平台自带的同步组件更灵活因为它对运行环境的要求极低——只要有JDK有Python执行入口脚本用就能跑起来。1.2 一次同步任务的基本运行逻辑很多人第一次接触DataX时会被它的目录结构吓到其实拆开看就两件事写配置、跑命令。一个完整的DataX同步任务本质上是定义了一条数据流转管道。你的核心工作就是写好一个JSON文件里面描述清楚三件事数据从哪来reader、数据写到哪去writer、以什么速率跑channel。DataX会解析这个JSON启动一个调度进程把任务拆分成多个并发线程通过内存缓冲区把数据从源头搬运到目标端。跑任务的命令也特别简单python datax.py job.json就这一行没有了。所有复杂的连接信息、字段映射、并发控制全部沉淀在JSON配置里。这也意味着你的所有“功力”都在于怎么把JSON写好、写对。我自己第一次跑通DataX时花了不少时间在“JSON怎么写才合法”上面。它有一套固定的结构比如最外层是jobjob下面是contentcontent里面是一个数组每个元素包含reader、writer、setting。这个结构看起来简单但嵌套层级一旦多起来少个括号、多个逗号任务就直接报错。所以后来我习惯了写完之后用Python的json.tool先校验一遍再做真实执行。2. 环境准备与安装部署2.1 最小化环境依赖DataX对运行环境的要求确实很低但“低”不代表“不用准备”。我实践下来的最小环境是JDK 1.8及以上版本推荐直接装OpenJDK。Python 2.7或3.x用来启动datax.py脚本。实测Python 3.6以上也没问题。目标数据源和源数据源各自的连接权限、账号信息、网络连通性。有足够的磁盘空间DataX会使用临时目录存放运行时产生的中间文件。安装就更简单了把DataX压缩包扔到服务器上解压之后目录结构长这样datax/ bin/ datax.py conf/ core.json logback.xml job/ job.json lib/ datax-core-xxx.jar datax-common-xxx.jar ... 各种数据源插件jar包 plugin/ reader/ mysqlreader/ hdfsreader/ ... writer/ mysqlwriter/ hdfswriter/ ... tmp/ log/plugin目录才是DataX的灵魂。每个数据源插件都是独立打包的互不干扰。如果你只需要MySQL到HDFS的同步甚至可以把不需要的插件删掉体积能瘦身不少启动速度也会快一点。不过新手不建议乱删等完全理解了每个插件的依赖关系之后再做精简。2.2 环境变量配置与启动验证解压之后推荐把DataX的目录配到PATH里。但我一般不爱改全局环境变量因为服务器上可能有多个版本共存的情况。我习惯在需要执行任务的用户目录下的.bashrc文件里写export DATAX_HOME/opt/datax export PATH$PATH:$DATAX_HOME/bin这样在任何目录下都能直接敲datax.py命令。配置完成之后先跑一下自带的自检脚本确认基础环境没问题python $DATAX_HOME/bin/datax.py $DATAX_HOME/job/job.json如果能看到“任务启动时刻”“任务结束时刻”“任务总计耗时”“平均流量”这些信息输出说明安装成功可以开始正式写任务了。提示如果你的服务器是内网环境没有外网那就不能使用在线安装的方式只能先把安装包传到服务器上解压。注意确认JDK的位数和版本出现过不少因为JDK版本太高导致部分插件加载不了的情况保守点直接用JDK 8最稳。3. 核心配置解析与JSON结构拆解3.1 一个标准任务JSON的完整框架先给一个我日常用得最多的模板MySQL同步到MySQL{ job: { content: [ { reader: { name: mysqlreader, parameter: { username: root, password: 123456, column: [*], connection: [ { table: [t_user], jdbcUrl: [jdbc:mysql://localhost:3306/test?useSSLfalsecharacterEncodingutf8] } ] } }, writer: { name: mysqlwriter, parameter: { username: root, password: 123456, writeMode: insert, column: [*], connection: [ { table: [t_user_bak], jdbcUrl: jdbc:mysql://localhost:3306/test?useSSLfalsecharacterEncodingutf8 } ] } } } ], setting: { speed: { channel: 3, byte: 1048576 } } } }这个JSON是理解所有DataX配置的钥匙。不管你后面用多少个Reader、多少个Writer底层表达方式完全一致。你只需要替换name字段和parameter里各个数据源专属的连接参数其他框架完全不用动。3.2 Reader和Writer的参数细节Reader部分的重点在三个地方怎么连接源端、读哪些列、以及并发切片机制。column字段既可以是通配符*也可以写具体的列名列表。我建议生产环境永远不要用*要显式列出列名。一方面防止源端表结构变更导致同步出错另一方面DataX在做字段映射时按名称匹配比按位置匹配更稳。connection下的table支持数组意味着你可以一次配置多张表。但要小心多表配置时DataX会按表顺序逐个读取如果各表结构不一致很可能报错。我习惯一次只同步一张表多张表就多写几个JSON配合调度平台并行跑。Writer部分的重点在writeMode。以mysqlwriter为例主要有三个取值insert直接插入、update按主键更新、replace存在则替换不存在则插入。这个选择直接决定了同步的幂等性策略。如果你做的是全量覆盖同步推荐用replace配合JobId能实现类似“按批次清理数据”的效果。如果是增量同步就用insert。还有一点容易被忽略Writer的jdbcUrl字段值是一个字符串而Reader的jdbcUrl字段值是一个数组。这个差异官方文档里写得很隐晦我当初调试了半天才发现是类型不匹配的问题。DataX的多个Reader插件和Writer插件之间参数格式并不完全对称宁可先看官方示例再改。3.3 Channel参数如何决定同步速率setting.speed.channel是控制并发度的核心参数。它是怎么运作的呢DataX会把Reader读到的数据放进内存队列Writer从队列里取数据写入目标端。channel的数量相当于同时跑了多少条读写管道。channel越多并行度越高同步速度越快但同时也意味着更高的内存消耗、更大的源端和目的端压力。我整理过一组实际经验值供你参考场景channel数说明小表几十万行以内1避免频繁建连够用即可中等规模千万级3-5兼顾速度和稳定性大表亿级5-10需要压测确认源端扛得住极端快速导入15以上只建议在内网高带宽场景使用另外channel到底是多少不能只看数据量还要看源端数据库的负载。我遇到过把channel调到10之后直接把源端MySQL的CPU打满的情况。那是一个业务核心库差点出事。所以我现在调并发时会遵循“逐步递增”的原则先用channel1跑一遍记录耗时和流量再把channel翻倍观察数据库监控指标稳定之后再继续往上加直到性能不再提升反而下降的那个点取上一个值作为最终配置。setting.speed.byte参数是流控用的如果不设置DataX会尽可能跑满带宽。对于生产任务建议设置一个合理的上限防止同步任务占满网络带宽影响线上业务。比如设置1048576单位是字节就是每秒最多1MB加上channel机制实际上最终速率是两者取较小值这个不要搞混了。4. 实操过程从零跑通一个同步任务4.1 场景设定MySQL数据同步到HDFS为了把一个完整的实操过程讲清楚我拿一个真实场景举例把业务库MySQL里的用户订单表同步到HDFS上按天分区存放做离线数仓的原始数据层。目标源表业务库testdb下的t_order目标HDFS路径 /warehouse/ods/t_order/dt20250115文件格式TextFile字段分隔符设为\t同步方式当天全量快照先看一下最终的job.json长什么样{ job: { content: [ { reader: { name: mysqlreader, parameter: { username: readonly, password: xxxx, column: [ order_id, user_id, product_id, order_amount, order_status, create_time ], splitPk: order_id, connection: [ { table: [t_order], jdbcUrl: [jdbc:mysql://10.0.0.x:3306/testdb?useSSLfalsecharacterEncodingutf8zeroDateTimeBehaviorconvertToNull] } ] } }, writer: { name: hdfswriter, parameter: { defaultFS: hdfs://10.0.0.y:9000, fileType: text, path: /warehouse/ods/t_order/dt20250115, fileName: t_order_, column: [ { name: order_id, type: long }, { name: user_id, type: long }, { name: product_id, type: long }, { name: order_amount, type: double }, { name: order_status, type: string }, { name: create_time, type: string } ], writeMode: append, fieldDelimiter: \t, compress: gzip } } } ], setting: { speed: { channel: 4, byte: 52428800 }, errorLimit: { record: 0, percentage: 0 } } } }4.2 关键参数的设计逻辑splitPk这个参数很关键。它告诉DataX用哪个字段作为分片键来切分并发任务。如果设置了splitPkDataX会在底层把一张表的数据按照这个字段的范围切成多段让多个channel并行读取。不设置的话即使channel配置了多个也只会在数据源层面做JDBC连接复用没法真正并行拉取。splitPk通常选择主键或数字类型的字段。因为DataX底层是用类似于between and的条件去分段读取数据varchar类型也可以但需要确认数据库端能做范围查询优化。我实测下来用自增主键效果最好切分均匀查询计划也稳定。hdfswriter的path路径我写的是具体到日期的分区路径。这跟Hive数仓的分区表结构是对应的后续如果要用Hive查数据直接通过分区路径就能映射到分区字段。还有一点要说明DataX在写入HDFS时不会自动创建多级目录所以你需要提前用hdfs dfs -mkdir -p把目录建好或者依赖调度平台在跑任务前执行建目录操作。writeModeappend表示追加写入不覆盖已有文件。对于每天同步的场景这个模式是安全的因为每天的目录是独立的不存在覆盖问题。如果同步的是业务上要覆盖的维度表可以考虑用truncate模式它会先清空目录再写入。4.3 从执行到验证的完整流程写好JSON后先校验格式python -m json.tool job.json没有报错说明语法没问题再执行python /opt/datax/bin/datax.py job.json执行过程中会实时输出每个channel的读取速度、写入速度、流量信息。我习惯盯着两个指标看读取记录总数确认跟源表行数一致。可以在源库跑一条count(*)两边核对。运行速度是否达到预期如果长时间在几十KB/s徘徊多半是数据库端慢查询或者网络有瓶颈。跑完之后的输出片段大致是这样任务启动时刻: 2025-01-15 01:00:00 任务结束时刻: 2025-01-15 01:08:32 任务总计耗时: 512s 任务平均流量: 18.67MB/s 记录写入速度: 32,500rec/s 读出记录总数: 16,658,432 读写失败总数: 0看到读写失败总数为0就可以收工了。下一步是去HDFS上看一眼文件是否正常生成hdfs dfs -ls /warehouse/ods/t_order/dt20250115/ hdfs dfs -cat /warehouse/ods/t_order/dt20250115/* | head -10我一般还会顺手跑一下文件行数统计用wc -l或者Hive里的count对比一下源表的行数确保两边一致。这一步看似多余但在真实生产里确实抓到过几次数据条数对不上的问题原因都是源端表在同步过程中有并发写入。5. 常见问题与排查技巧实录5.1 连接超时与连接被拒最常见的坑就是数据源连不上。报错信息一般长这样Caused by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure我的排查顺序是固定的先在本机用mysql命令测试一下能不能连上目标库排除网络问题。检查jdbcUrl里的IP和端口是否真的开放了。很多时候是安全组策略没放行这个在云上环境尤其常见。确认账号权限没有问题。DataX连接数据库的账号需要有SELECT权限写端还要有INSERT、UPDATE、DELETE、CREATE等权限缺一不可。检查数据库的max_connections有没有打满。如果数据库连接池已经被占满DataX的连接就会被拒。还有一个容易忽视的细节MySQL连接串里要带useSSLfalse。内网数据库一般都没开SSL如果这里不写连接初始化阶段会花很长时间甚至直接hang住。5.2 数据格式容忍度问题有一段时间我同步MySQL的数据到HDFS时老报错日志提示java.lang.NumberFormatException: For input string: abc排查发现源表某个int字段里混入了脏数据出现了字符串类型的值。DataX默认遇到类型转换失败就会报错除非在setting里配置errorLimit。errorLimit有record和percentage两个字段含义分别是可以容忍的错误条数和错误比例。生产环境我一般不设容忍度因为脏数据意味着数据质量有问题宁可同步失败暴露出来也不要静默丢掉。但在做一次性数据迁移时我会把errorLimit的percentage设为5先保证任务能跑完拿到绝大部分数据再回头单独清洗脏数据。5.3 HDFS权限与文件格式坑HDFS写入报错最常见的原因是权限不足。DataX进程以什么系统用户启动HDFS上对应的就是什么身份。如果目标目录的owner和权限没有针对这个用户放通就会遇到Permission denied。解决办法有几个给目标目录设置递归权限hdfs dfs -chmod -R 777 /warehouse/ods或者把DataX进程的启动用户切换到HDFS的超级用户或者用Kerberos认证企业集群场景我在测试环境一般用第一种生产环境会专门创建一个数据同步账户配置好它的读写权限避免用hdfs超级用户跑所有任务。fileType这个参数也经常出错。hdfswriter支持text和orc两种格式。text格式就是普通文本文件调试方便任何工具都能读。orc格式是列式存储压缩率高、查询性能好适合数据量很大的场景。但如果你的下游是Spark或Presto建议直接用orc省掉后续转换的麻烦。不过要注意orc格式对字段类型要求更严格string和varchar不要混用否则会出现读出来类型不匹配的问题。5.4 同步速度远低于预期这个是群里被问得最多的一个问题。明明channel配了8结果速度只有几十KB/s。我总结下来主要因素不外乎这几个splitPk没设置导致并行读根本没生效。源库是主从架构同步账号连接到了只读节点只读节点的负载太高查询性能大幅下降。目标端HDFS的DataNode磁盘IO打满了。JDBC连接串里少了rewriteBatchedStatementstrue导致批量写入没有生效。这个参数对MySQL写入性能影响巨大加上它之后写入速度能提升好几倍。实测过一组对比同样的表同样的channel数加上rewriteBatchedStatementstrue之后写入速度从3万条/秒提升到12万条/秒。这个参数对于MySQL写入场景几乎是必选项。提示MySQL连接串中建议同时使用rewriteBatchedStatementstrue和useCompressiontrue前者优化批量写入后者在数据量大的时候减少网络传输开销。5.5 多表同步时的类型映射不同数据源之间的类型映射是另一个大坑。DataX有一套自己的类型体系比如long、double、string、date、bytes、boolean它在读取阶段会把源端类型转换成自己的类型在写入阶段再把内部类型转换成目标端类型。问题是这种转换规则在不同插件里并不完全一致。比如MySQL的datetime类型在mysqlreader里会转成date但hdfswriter的date类型和string类型的处理方式不同。如果不小心同步过去之后时间字段会变成一串数字或者格式不对。我的避坑方法是在writer的column里显式指定目标端类型不要依赖自动推断。尤其是时间类型目标端要指定成string并在读端就做好格式转换。可以在SQL里直接写date_format(create_time, %Y-%m-%d %H:%i:%s) as create_time这样源头输出就是标准格式字符串写端直接按string处理完全避开类型转换的坑。5.6 常见错误速查表现象可能原因解决思路任务启动即报ClassNotFoundlib目录缺插件jar包重新完整解压DataX检查plugin目录读取速度极慢未设置splitPk按主键设置splitPk写入速度极慢缺少rewriteBatchedStatementsMySQL连接串加上该参数JSON解析报错字段类型写错或缺少逗号用python -m json.tool校验目标端重复数据writeMode用错更新MySQL写模式为replaceHDFS路径不存在未提前建目录调度流程里加mkdir步骤内存溢出channel数量过多或byte限制过大降低channel设置byte流控6. 一些心得体会用了DataX这么久最大的体会是它的定位很精准它不搞服务化不做Web界面不负责调度只专注做一件事——把数据稳定地从A搬到B。这种“单点做好”的思路反而让我在排障时特别舒服因为每个环节都单一、透明出问题就能快速定位。我现在的做法是把所有同步任务按业务线组织成独立目录每个任务一个JSON。在调度平台里按天触发跑完自动记录日志。长期运行下来非常稳定基本不需要人为干预。DataX的另一个好用之处是它的插件机制。如果现有插件满足不了需求你甚至可以自己写一个插件只要继承它提供的接口打包放进plugin目录就能被识别。有一次我需要从一个内部自研的存储系统同步数据就是照着官方插件的代码结构自己写了个reader整个过程大概花了一天时间这种自由度是很多商业同步工具给不了的。最后分享一个小技巧DataX在跑任务的时候日志里会打印每个channel的实时流量如果发现某个channel长期没有输出大概率是数据倾斜了。这时候可以用splitPk配合一个更均匀的字段比如把字符串主键换成自增主键或者增加多个字段拼接成切分键基本能解决。如果你是刚接触DataX建议先从最简单的MySQL到MySQL开始跑通了再往上叠加HDFS、Hive这些复杂目标端。每一步都确认数据一致之后再扩展场景就不会在踩坑中浪费时间。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表