
简介面向需要基于 DataX 完成 MySQL 8.x 数据同步的开发与运维人员这份资源提供了可直接使用的 mysqlreader 与 mysqlwriter 两套插件解决官方 DataX 默认插件对 MySQL 8 驱动与认证方式兼容不佳的问题。通过内置的插件配置模板和 job 示例用户可快速接入现有 DataX 环境完成分片并行读取、批量写入及主键冲突策略设置提升同步效率与数据一致性。资源共 36 个文件主要由 32 个 jar 包和 4 个 json 模板组成包含 mysql-connector-java 8.0.25、druid、fastjson 等运行依赖压缩包大小约 19.23MB可直接放入 DataX 的 plugins 目录使用。包内目录按 reader 与 writer 拆分清晰模板可直接修改便于二次开发和排错。已有 1765 人学习下载适合需要为 DataX 扩展 MySQL 8 支持、或正在搭建数据同步管道的读者参考。1. 为什么DataX连不上MySQL 8驱动与认证的底层变化1.1 先分清对象DataX的“插件”到底是什么很多人听到“datax读写MySQL8的插件”第一反应是去网上找一个叫“datax-mysql8-plugin”的东西下载。这个方向其实偏了。DataX的插件机制指的是它的 reader/writer 插件体系也就是plugin/reader和plugin/writer目录下那一堆子目录——比如mysqlreader、mysqlwriter、oraclereader、hdfswriter等。每个插件目录里都放着对应的jar包、配置文件和一个plugin.json。DataX在运行时通过plugin.json描述的入口类去加载读写逻辑所以所谓“让DataX支持MySQL8”本质上是让mysqlreader和mysqlwriter这两个已有的内置插件能够正确连上MySQL 8的服务端。我一开始也被“插件”这个词绕了一下以为要重新编译或者安装什么额外组件。实际搞清楚以后发现DataX本身不自带MySQL 8专用的插件但它自带的mysqlreader/mysqlwriter插件完全可以复用只要把底层JDBC驱动换掉再处理几个连接参数就行了。这篇文章就把这条路完整地走一遍。1.2 连接失败的两大根因如果你在DataX里直接配置一个MySQL 8的数据源大概率会碰到两类错误。第一类是ClassNotFoundException: com.mysql.jdbc.Driver或者Unable to load authentication plugin caching_sha2_password。原因是DataX自带的mysql-connector-java版本太老通常是5.1.x老驱动不认识MySQL 8默认的caching_sha2_password认证插件。MySQL 8.0开始默认认证方式改成了caching_sha2_password5.x的驱动包里压根没有实现这个认证协议的类所以握手直接失败。第二类是连接超时、时区错误、SSL握手失败等异常现象五花八门比如The server time zone value йʱ is unrecognized Public Key Retrieval is not allowed Communications link failure这些错误本质上是驱动版本旧 连接参数缺省导致的连锁反应。MySQL 8的JDBC驱动在连接串里多了很多默认行为开关比如allowPublicKeyRetrieval、useSSL、serverTimezone老版本驱动没有对应的处理逻辑或默认值和新版服务端不兼容就会在连接阶段翻车。所以解决方案的核心就两条换驱动、补参数。下面我会把步骤、配置和踩坑链路完整写出来。2. 环境准备把MySQL 8正式纳入DataX插件体系2.1 确认已安装的DataX与插件目录开始之前先确认你的DataX目录结构和版本。以最常见的datax解压目录为例datax/ ├── bin/ │ └── datax.py ├── conf/ │ ├── core.json │ └── logback.xml ├── job/ │ └── (自定义job json放这里) ├── plugin/ │ ├── reader/ │ │ └── mysqlreader/ │ │ ├── libs/ │ │ │ └── mysql-connector-java-5.1.47.jar │ │ ├── plugin.json │ │ └── plugin_job_template.json │ └── writer/ │ └── mysqlwriter/ │ ├── libs/ │ │ └── mysql-connector-java-5.1.47.jar │ ├── plugin.json │ └── plugin_job_template.json └── tmp/注意mysqlreader和mysqlwriter是两套独立的插件目录各自有自己的libs文件夹。也就是说同一个驱动jar包你要放两遍——reader一份writer一份。只替换其中一个另一个还是会用旧驱动去连接MySQL 8照样报错。我见过有人在reader的libs里换了新驱动writer忘了换然后同步任务从MySQL 8读出来写到另一个MySQL 8时写端一直连不上。这种问题不看目录结构很难排查因为它报错的时间点是在writer初始化阶段而reader已经正常工作了容易让人误以为是writer配置写错了。2.2 准备MySQL 8驱动并替换/新增jar包推荐使用mysql-connector-java-8.0.x版本比如8.0.28或8.0.33。不要用更早的8.0.11之类部分早期8.0驱动对caching_sha2_password的实现有坑8.0.20以后稳定很多。下载方式有两种# 方式一直接从Maven中央仓库下载 wget https://repo1.maven.org/maven2/mysql/mysql-connector-java/8.0.33/mysql-connector-java-8.0.33.jar # 方式二用maven命令拉取后拷贝适合没有wget的Windows环境 mvn dependency:get -Dartifactmysql:mysql-connector-java:8.0.33拿到jar包后分别放到plugin/reader/mysqlreader/libs/和plugin/writer/mysqlwriter/libs/下。这时目录里会同时存在mysql-connector-java-5.1.47.jar和mysql-connector-java-8.0.33.jar两个包。我建议直接把老包删掉只保留新包避免类加载冲突。提示DataX插件加载libs时是把目录下所有jar都塞进classpath的新旧驱动同时存在时类加载顺序不固定你没法保证老驱动先被扫到还是新驱动先被扫到。稳妥做法就是删掉旧的只留一个新的。如果你是用源码构建DataX的方式运行那就要在core/src/main/java对应依赖里改pom版本自己重新打包插件。生产环境通常直接改安装目录更快不用动源码。2.3 顺带说下MySQL 8安装时容易忽视的认证选项如果你是在测试环境全新安装MySQL 8有个点要提前想清楚。MySQL 8.0默认认证插件是caching_sha2_password而这个认证方式在公网明文传输时需要额外的TLS或RSA公钥交换支持。如果你用的是老版本的客户端工具比如老Navicat、老DataGrip经常会连不上而用新版驱动就没问题。但有些场景下出于兼容性考虑你会选择在创建用户时直接指定老认证方式CREATE USER datax_user% IDENTIFIED WITH mysql_native_password BY your_password; GRANT SELECT, INSERT, UPDATE, DELETE ON your_db.* TO datax_user%;注意这是MySQL 8的一项变化mysql_native_password不再是默认选项需要显式声明。如果你没指定默认就是caching_sha2_password。对DataX而言8.0驱动两种认证都支持所以不指定问题也不大只要驱动版本新就行。不过如果用户权限只开放了部分库表GRANT语句要和你实际同步的库一致这个和MySQL 5.7时代是一样的逻辑。3. 可复用的json配置从读MySQL8到写MySQL83.1 直接从MySQL8读数据的reader配置基础配置可以直接参考plugin/reader/mysqlreader/plugin_job_template.json。这个模板文件里的占位符比较粗糙真正用的时候还是要自己写job。下面是一份读MySQL 8的reader片段我加了注释方便理解{ job: { content: [ { reader: { name: mysqlreader, parameter: { username: datax_user, password: your_password, column: [ id, name, created_at ], connection: [ { table: [ source_table ], jdbcUrl: [ jdbc:mysql://127.0.0.1:3306/source_db?useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/ShanghaiuseUnicodetruecharacterEncodingutf8 ] } ], where: id 100000, splitPk: id } } } ], setting: { speed: { channel: 4 } } } }关键点逐个说。jdbcUrl里useSSLfalse是很实用的参数。测试环境MySQL 8默认虽然开启了支持TLS但真正常见的做法是关掉SSL省去证书配置的烦恼。allowPublicKeyRetrievaltrue是针对caching_sha2_password的因为该认证方式在非SSL连接下需要从服务端获取RSA公钥新驱动默认禁止这种“隐式公钥检索”必须显式打开。serverTimezoneAsia/Shanghai解决时区问题——老版本驱动遇到服务端time_zone异常值会直接报错干脆在连接串里定死时区。column是字段列表建议写全字段名不要用*。原因是DataX拿这个字段列表做SELECT拼装和结果集映射使用*时字段顺序和表结构强绑定一旦源表加了字段结果集列数和列名就可能对不上任务直接失败。where可选用于增量抽取或过滤通常结合上次同步的max(id)或时间戳使用。splitPk用于切分任务分片是并行读取的关键后面调优部分细说。3.2 写入MySQL8的writer配置及writeMode选择writer配置对应的模板是plugin/writer/mysqlwriter/plugin_job_template.json。完整片段如下{ writer: { name: mysqlwriter, parameter: { username: datax_user, password: your_password, writeMode: insert, column: [ id, name, created_at ], connection: [ { table: [ target_table ], jdbcUrl: jdbc:mysql://127.0.0.1:3306/target_db?useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/ShanghaiuseUnicodetruecharacterEncodingutf8 } ] } } }writeMode是MySQL writer最需要搞懂的参数它支持三个值insert、replace、update对应底层SQL行为insert执行INSERT INTO ... ON DUPLICATE KEY UPDATE或普通INSERT取决于是否有主键/唯一键冲突。没有冲突就是纯插入有冲突会走更新逻辑。replace执行REPLACE INTO ...遇到主键/唯一键冲突时先删除旧行再插入新行。代价是自增id会变且删除动作会触发相关索引维护性能略低。update执行UPDATE ...只更新已存在的行不会插入新行。这个值很多时候被当成“写入模式开关”随手填了个insert实际要根据同步语义决定。比如从A表同步到B表做全量镜像用replace最稳做增量追加用insert只修复目标表已有行的某些列用update。batchSize参数默认在core.json里是1024表示一批写入的行数。MySQL 8对大事务的容忍度不高特别是开启了binlog的行复制模式单批过大可能拖慢主从同步或触发锁等待。后面调优时我会给出经验值。3.3 column、splitPk、batchSize这些参数到底在调什么有些同学把DataX的job配置当成“能跑就行”但调优时如果不知道每个参数底层影响什么就只能瞎试。我拎出三个最常被误解的说清楚。column在前端看是字段名单在底层它决定了SELECT语句和PreparedStatement参数绑定顺序。DataX会把你列的字段名逐个填入SELECT col1, col2, col3 FROM ...读出的数据在内存里按列顺序组成Record。所以如果你手写了一个querySql覆盖column那么querySql返回的列顺序必须和column里声明的一致否则writer写入时字段错位错得很隐蔽数据对不上才发现。splitPk说的是“切分主键”。DataX在channel 1 时按什么逻辑把一个表拆成多段并行读就是靠这个字段。它会把splitPk指定的列做 min/max 查询然后按channel数均分成区间每个channel读一个区间段。因此这个字段要选一个有索引的数字列最好就是主键。选splitPk的第一步不是看它“能不能用”而是看它“有没有索引”。如果你把一个无索引的普通列设为splitPk并行度上来以后每个区间段的WHERE id BETWEEN ? AND ?都会走全表扫描CPU和IO直接拉满任务反而更慢。batchSize则是在writer端生效。DataX攒够batchSize条记录执行一次批量写入addBatchexecuteBatch。这个值决定了一次网络往返写入的数据量。MySQL 8的max_allowed_packet默认是64MB但实际批量写太快容易导致主从复制延迟增大特别是binlog为ROW格式时。一般batchSize在 512~2048 之间比较合理不用刻意贪大。4. 踩坑实录从ClassNotFound到“Public Key Retrieval is not allowed”4.1 第一坑驱动类名和jar包版本不匹配驱动换好后第一个要检查的是driverClassName。DataX的mysqlreader和mysqlwriter默认在代码里写死了驱动类名老版本插件是com.mysql.jdbc.Driver新驱动8.0的类名是com.mysql.cj.jdbc.Driver。DataX的插件并没有在json里提供driverClassName配置项这一点和某些框架不同所以类名写死在插件源码里。那么问题来了旧插件代码里写的是com.mysql.jdbc.Driver而新版驱动jar包虽然为了兼容保留了com.mysql.jdbc.Driver这个类它内部转调com.mysql.cj.jdbc.Driver但如果你用的是修改版插件或某些第三方魔改DataX很可能驱动类名还是旧的就会报类似ClassNotFound: com.mysql.cj.jdbc.Driver遇到这种情况最直接的验证方式是看plugin/reader/mysqlreader/libs/下jar包里的Driver类jar tf mysql-connector-java-8.0.33.jar | grep com/mysql/cj/jdbc/Driver.class只要这个类存在数据源配置正确其实不会走到ClassNotFound这一步。真出现ClassNotFound九成是插件jar包里的代码写死了老类名且没有兼容跳转。解决办法要么换官方原版插件要么直接改插件源码重新编译。大部分情况下用官方自带的mysqlreader/mysqlwriter就不会有这个问题。4.2 第二坑时区与SSL引发的各类Connection异常换完驱动后最容易踩的第二个坑是连接串参数缺失导致的时区或SSL异常。典型报错java.sql.SQLException: The server time zone value йʱ is unrecognized or represents more than one time zone.这个乱码一看就是中文环境下的Asia/Shanghai或CST时区没被老版本驱动正确识别。新驱动在连接串里显式指定serverTimezoneAsia/Shanghai后问题基本消失。但如果Linux系统时区文件没配好Asia/Shanghai也可能解析失败此时可以换成serverTimezoneGMT%2B8这是URL编码后的GMT8不会有时区库依赖。SSL相关的报错更多样常见的是SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)MySQL 8默认版本启用了TLS但DataX运行环境特别是JDK 8的TLS协议和MySQL 8默认配置之间可能不匹配。最稳妥的做法是在连接串里显式useSSLfalse如果是生产环境强制要加密那就要去MySQL端配置SSL证书并把客户端的useSSLtruetrustCertificateKeyStoreUrl...等参数补齐工作量会大很多。就数据同步工具而言很多团队跑在内网专用网络里useSSLfalse是常态。4.3 第三坑字段类型映射与分隔符的理解偏差MySQL 8和MySQL 5.7在字段类型上差异不大但DataX读取时对某些类型的处理容易让人困惑。比如TIMESTAMP类型MySQL 8支持到微秒精度DATETIME(6)DataX默认按java.sql.Timestamp处理本身没问题但如果你在json里把该列简单映射为String可能会丢失微秒部分。同步复杂类型时要留意目标表字段的精度是否和源表一致。另外热词里有一个“字段分割符是什么如何改”。这个在DataX语境下通常指的是文本类reader/writer比如txtfilereader/txtfilewriter里的fieldDelimiter配置项。它和MySQL同步无关——MySQL本身不是文本文件字段间没有分隔符的概念。如果你是在MySQL同步场景里想控制“一列里多个值”怎么分那要处理的是列类型转换不是分隔符。我在群里看到有人把CSV同步的fieldDelimiter概念套到mysqlreader上找了半天没找到配置项最后发现压根是两码事。搞清楚这点能省不少时间。5. 从“能跑”到“跑得快”并发与重试的调优心得5.1 channel和splitPk的关系DataX的并行度由setting.speed.channel决定但channel不是开得越多越快。channel数量只是DataX进程内“线程通道”的上限真正决定一个表能否被拆分并行读取的是reader端是否配置了有效的splitPk。没有splitPk时channel再多一个表的读取也只能在一个通道上串行跑有splitPk时DataX会对该字段做区间切分把不同区间分配到不同channel。所以我测试时的经验是先把splitPk配好再来调channel数不要反过来。channel过多还会带来一个问题每个channel独立持有数据库连接MySQL 8默认的max_connections是1515.7版本也是如果DataX的channel开到10还有其他应用在连库很容易把连接数打满。遇到Too many connections报错时先别急着调大MySQL的max_connections看看是不是DataX的channel数设得不合理。5.2 批量参数对MySQL8写入吞吐的影响写入端性能主要由batchSize和channel的组合决定。我做过一次对照测试源表约500万行目标表是MySQL 8InnoDB引擎环境相同batchSizechannel耗时备注5122约3分20秒binlog复制正常10242约2分50秒自增id连续性OK20484约1分40秒主从复制有一定延迟40964约1分35秒延迟进一步加大锁冲突提示增多结论很明显batchSize从512调到2048收益最大再往上涨收益递减channel从2调4有明显收益但4以上要看目标表有没有其他并发写入。如果目标表有实时业务在写channel和batchSize最好保守一点别为了同步速度把线上业务拖垮。另外提一句DataX在写入MySQL时如果出现死锁或锁等待超时任务会默认重试。重试次数在core.json里的相关参数控制。我在生产环境遇到过一次Deadlock found when trying to get lock单靠DataX默认重试就能自愈不用改代码。但如果重试后仍然失败就要考虑降低channel数或batchSize来减少锁竞争。5.3 内存与连接数的权衡经验DataX是Java进程内存由JVM参数控制默认在bin/datax.py里设置了-Xms1g -Xmx1g。当channel开得多、batchSize调大时内存里同时驻留的记录也会变多。如果同步的表单行数据很大比如有MEDIUMBLOB或TEXT字段1G堆内存很容易吃紧任务会频繁Full GC甚至OOM。我的经验是把-Xmx2g或-Xmx4g作为首选并且不要只调大内存而不关注batchSize。内存压力主要来自读端积压的RecordbatchSize影响的是写端攒批所以如果你发现GC频繁优先减小channel而不是减batchSize。读得慢、写得快通道里积压的数据少内存压力反而小。连接数方面DataX每个reader channel会持有一个MySQL连接每个writer channel也会持有一个。同步两库时最大连接数大致等于channel * 2加上MySQL连接池预留。配置channel前先数一下源库和目标库的max_connections给自己留出至少30%的余量。最后再分享一个排查小技巧。DataX出问题时直接看$DATAX_HOME/log下的同步日志里面会记录每个channel的启动、读取条数、写入条数和耗时。如果某个channel的读写条数明显为0大概率是splitPk切分区间不均某个区间的数据量特别大或特别小。这时可以手动查一下MIN(splitPk)和MAX(splitPk)的分布适当地调整channel数来让区间切得更均匀。同步工具这东西跑通只是第一步把参数调到和你的数据分布匹配才是真正省心的时候。本文还有配套的精品资源点击获取