你是不是也在做数据中台折腾过一堆组件之后发现最头疼的不是中台框架本身而是底层那些零零散散的数据同步任务传统DataX同步个千万级表要卡半天Canal又只管增量两套一起上维护成本翻倍。我最近在把自建的AllData数据中台和Seatunnel-Web做了一次深度集成专门解决MySQL到Doris的异构同步问题——全量增量一把梭Web界面点点鼠标就能跑连SQL转换都可以在页面上直接配。这篇文章把我从选型、部署到跑通全流程的实操细节全部摊开包括那些文档上不会写的坑。1. 整体方案设计为什么我放弃了DataX和Canal选了Seatunnel-Web1.1 同步痛点与选型对比先说业务背景。我们中台的数据源是线上业务库MySQL目标端是Doris数仓。需求其实很朴素每天凌晨全量同步核心表白天增量捕获变更同时要支持多表批量操作和清洗转换。以前我同时挂了DataX和CanalDataX负责小时级全量Canal监听binlog再把变更推到Kafka最后由Flink任务消费写入Doris。这套链路跑了大半年出过不少幺蛾子。DataX本身是个好东西单机跑全量吞吐量确实猛但一到增量就得配合定时调度而且如果你用的是DataX的版本对Doris的写入支持很一般。Canal Flink这套链路太长组件太多一个节点挂了整条链路都得查一遍。后来我调研了一圈把目光锁定在Apache SeaTunnel上。它的核心吸引力对比项DataXCanal FlinkSeaTunnel含Web全量同步支持不支持支持增量同步不支持需额外调度支持支持binlogWeb管理界面无靠DataX Web二次开发需自研Seatunnel-Web原生多源多目标一对一为主需自定义多对多插件化Doris写入优化一般需自写Sink原生Stream Load支持部署复杂度低高中1.2 AllData中台为何要集成Seatunnel-WebAllData这个数据中台底层集合了十几个开源组件从数据采集、同步、计算到治理都有对应的模块。但如果你只用它自带的数据采集模块会发现调度编排和可视化做得还不够顺手。Seatunnel-Web恰好补上了这块短板——它提供了独立的后端服务和前端界面可以单独部署也可以作为中台的同步服务嵌入。我选择在AllData中单独搭建Seatunnel-Web实例再通过中台的统一入口把同步任务的管理权限接进来。这样做的好处是同步任务可视化不用再去翻SeaTunnel的配置文件在Web界面填数据源、选表、配同步模式就行。任务调度内置增量、全量、定时都有现成的调度机制中台侧只需要关注任务状态回调。转换逻辑还在虽然集成了Web但SQL过滤、字段映射这些配置项保留在任务里适合做轻度清洗。2. 环境搭建MySQL、Doris与Seatunnel-Web的安装部署2.1 MySQL安装与配置要点因为我们的同步源端是MySQL所以先把MySQL环境准备好。我用的是Linux服务器上二进制方式安装MySQL 8.0。# 下载MySQL二进制包并解压到/usr/local/ wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.36-linux-glibc2.17-x86_64.tar.xz tar -xvf mysql-8.0.36-linux-glibc2.17-x86_64.tar.xz -C /usr/local/ mv /usr/local/mysql-8.0.36-linux-glibc2.17-x86_64 /usr/local/mysql # 创建mysql用户和目录 groupadd mysql useradd -r -g mysql -s /sbin/nologin mysql mkdir -p /usr/local/mysql/data chown -R mysql:mysql /usr/local/mysql顺手把配置文件/etc/my.cnf写好[mysqld] server-id1 log_binmysql-bin binlog_formatROW gtid_modeON enforce_gtid_consistencyON port3306 bind-address0.0.0.0这里是关键——同步源端MySQL必须开启binlog且格式必须设为ROW。Seatunnel的MySQL CDC功能依赖binlog解析如果是STATEMENT格式遇到某些大事务可能解析不完整。初始化并启动MySQL之后还要做两个必要设置# 创建同步专用账号并给权限 CREATE USER seatunnel% IDENTIFIED BY Your_Strong_Pwd; GRANT SELECT, SHOW VIEW, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO seatunnel%; FLUSH PRIVILEGES;2.2 Doris安装与集群配置目标端Doris我选择的是2.1.x版本Doris在1.2之后架构变化很大新版本对Seatunnel的兼容性更好。Doris本身不依赖其他第三方组件我直接单FE、单BE起步后续再扩容。Doris部署需要注意的点FE元数据目录和BE数据目录分别建好不能共用同一目录。be.conf里的mem_limit要根据机器真实内存设置我之前默认配了80%结果和JVM抢内存直接把BE干崩了。Doris默认端口8030是Web UI8040是BE的Stream Load端口。Seatunnel写Doris就是走8040端口别在防火墙里忘了放行。给同步用建一个目标表CREATE TABLE IF NOT EXISTS dwd_order_info ( order_id BIGINT, user_id BIGINT, product_code VARCHAR(32), pay_amount DECIMAL(10,2), order_status INT, create_time DATETIME, update_time DATETIME ) DUPLICATE KEY(order_id) DISTRIBUTED BY HASH(order_id) BUCKETS 10 PROPERTIES (replication_num 1);这里我踩过一个坑一开始把这份表建成了UNIQUE KEY模型想着反正业务上order_id唯一。结果做全量回放更新的时候Doris的MOW合并性能反而拖慢了。对于大多数同步落地场景DUPLICATE KEY模型足够后续靠视图或物化视图去重即可。2.3 Seatunnel-Web安装全流程Seatunnel-Web实际上是两个部分SeaTunnel引擎负责执行同步任务Web服务包含后端接口和前端页面。阿里云知情人士透露Seatunnel-Web的版本对应关系容易混淆我以seatunnel-web-1.0.1版本为例讲。先准备好JDK环境1.8即可如果跑Flink引擎就要JDK11但我们用的是自带的Zeta引擎# 从官网release页面下载seatunnel-web安装包 wget https://archive.apache.org/dist/seatunnel/seatunnel-web/1.0.1/apache-seatunnel-web-1.0.1-bin.tar.gz tar -zxvf apache-seatunnel-web-1.0.1-bin.tar.gz cd apache-seatunnel-web-1.0.1初始化数据库Web服务需要自己的元数据库# Seatunnel-Web内置了一套初始化SQL存放在conf/db目录下 mysql -uroot -p conf/db/seatunnel_server_mysql.sql mysql -uroot -p conf/db/seatunnel_console_mysql.sql接着修改conf/application.yml把数据库连接指向刚建好的元数据库。然后启动Web服务sh bin/start-backend.sh服务起来后浏览器访问http://your-server:8080就能看到登录界面。默认账号密码是admin/admin登录后务必在用户管理里改掉默认密码。注意Seatunnel-Web的前端是打包在安装包里直接由Nginx托管的你只需要确认8080端口没有别的进程占用。2.4 AllData与Seatunnel-Web的对接思路这里很多第一次接触的人会卡住因为AllData是个大而全的中台Seatunnel-Web是个独立的同步服务两者如何“集成”各有各的做法。我的做法比较直接AllData的数据源模块里添加MySQL和Doris的配置信息但同步执行引擎改为调用seatunnel-web的API。具体来说AllData侧注册数据源连接信息存一份到自己元数据库。同步任务创建时把源端、目标端和同步配置已JSON报文的形式提交给Seatunnel-Web的/api/v1/task/save接口。任务执行状态通过Seatunnel-Web的/api/v1/task/execute接口触发再定时拉取任务状态。相当于AllData做上层调度和入口Seatunnel-Web做底层执行引擎。这样既保留了中台统一管理的体验又复用了Seatunnel-Web成熟的作业调度和错误重试能力。3. MySQL到Doris同步实操从配置数据源到跑通全量增量3.1 在Seatunnel-Web中配置源端与目标端登录Seatunnel-Web管理界面后先进入“数据源管理”页面。源端MySQL的配置项如下配置项填写值说明数据源类型MySQL主机名192.168.1.10业务库地址端口3306用户名seatunnel前面建好的同步账号密码对应密码database默认库名可以为空任务里再指定目标端Doris的配置项配置项填写值说明数据源类型Doris主机名192.168.1.20FE地址端口9030MySQL协议端口Seatunnel通过该端口连接Doris用户名root密码doris-root-pwddatabasedwd目标库名填完之后有个小细节先点“测试连接”验证两个数据源都通了再保存。这个界面在后端会做一次连通性检查如果MySQL那边网络隔离导致连不上测试会直接红字提示这比任务跑到一半了再报错要划算得多。3.2 创建同步任务全量同步配置接下来创建同步任务。在“任务管理”页点击新建任务选择数据同步类型为离线同步全量。关键配置项源端选项选择MySQL数据源指定schema为order_db表名填order_info。如果你要同步多张表可以填正则比如order_.*Seatunnel会批量匹配。目标端选项选择Doris数据源数据库dwd表名填dwd_order_info。如果目标表还不存在勾选“自动建表”它会根据源表字段类型自动映射生成Doris建表语句。读取并行度这里默认是1但对于大表我建议调高到4~8。并行度会直接拆分SQL查询多个线程同时拉数据。保存后任务会在里面生成一个配置JSON你可以点击“编辑JSON”看看长什么样。核心片段类似这样{ env: { parallelism: 4, job.mode: BATCH }, source: { plugin_name: Jdbc, url: jdbc:mysql://192.168.1.10:3306/order_db, user: seatunnel, password: Your_Strong_Pwd, query: SELECT order_id, user_id, product_code, pay_amount, order_status, create_time, update_time FROM order_info }, transform: { plugin_name: Copy }, sink: { plugin_name: Doris, fenodes: 192.168.1.20:8030, username: root, password: doris-root-pwd, table.identifier: dwd.dwd_order_info, source.use.body.delimiter: true, column_separator: \\x01, sink.max.retries: 3, sink.enable.batch-mode: true } }3.3 创建增量同步任务基于binlog的CDC增量同步是重头戏。在Seatunnel-Web中新建任务时同步类型选实时同步源端会切换成CDC模式。数据源类型选择 MySQL CDC 启动模式选择initial首次启动会先做一次全量快照然后接续binlog增量高级配置里有几个值得注意的参数snapshot.mode建议用initial。它会自动对当前表数据做一致性快照然后切到增量监听好处是不用手动协调全量和增量的衔接。startup.mode如果你确定binlog位点可以指定specific_offset对应填写binlog文件名和位置偏移量。server-id这里要特别注意。Seatunnel CDC每启动一个任务会占用一个MySQL server-id如果同一个实例上多个任务用了相同server-idMySQL服务端会强制杀掉连接任务就会反复失败。建议每个任务单独指定一段范围比如任务A用5400-5404任务B用5405-5409。增量任务JSON核心片段{ env: { parallelism: 1, job.mode: STREAMING }, source: { plugin_name: MySQL-CDC, hostname: 192.168.1.10, port: 3306, username: seatunnel, password: Your_Strong_Pwd, database-names: [order_db], table-names: [order_db.order_info], server-id: 5400-5404, startup.mode: initial }, sink: { plugin_name: Doris, fenodes: 192.168.1.20:8030, username: root, password: doris-root-pwd, table.identifier: dwd.dwd_order_info, sink.enable.batch-mode: true, sink.label-prefix: dwd_order_info } }Doris sink参数里的sink.label-prefix建议设置得有点辨识度因为Doris的Stream Load为了保证精确一次性靠label保证幂等。如果两个同步任务共享同一个label前缀在极端情况下Doris端会拒绝其中一个任务的写入报Duplicate label错误。3.4 执行任务与结果验证全量同步任务保存后点“运行”按钮。第一次跑建议先看任务日志逐行跟着看。我这次同步的order_info表全量180万行4并行度下耗时约40秒吞吐量差不多45000行/秒。对于MySQL到Doris这个路径来说算不错的成绩。增量任务启动后Seatunnel-Web的任务列表里会显示“运行中”状态并且监控面板能看到当前消费的binlog位点。打开Doris的监控页面执行SELECT COUNT(*) FROM dwd.dwd_order_info; SELECT MAX(update_time) FROM dwd.dwd_order_info;确认数据已经在持续更新。为了模拟真实的增量场景我在MySQL侧往源表插入了3条测试数据INSERT INTO order_db.order_info (order_id, user_id, product_code, pay_amount, order_status, create_time, update_time) VALUES (1800001, 10086, SKU-001, 99.90, 1, NOW(), NOW()), (1800002, 10087, SKU-002, 199.00, 2, NOW(), NOW()), (1800003, 10088, SKU-003, 299.00, 3, NOW(), NOW());Doris侧隔了几秒再查发现这3条已经同步过去了。说明binlog消费链路没问题。4. 踩坑记录与疑难排查同步链路中的实际问题速查4.1 Doris“内存不足”导致BE崩溃这是我在同步中最常被恶心到的问题。Doris BE进程在同步大批量写入时有时候会直接OOM。日志里能看到类似memory limit exceeded排查下来发现是我be.conf里的mem_limit设置太大了占物理内存85%同步进程启动时BE还需要为每个导入任务分配额外的Stream Load buffer内存直接被挤爆。解决办法把mem_limit调低到50%并限制单次Stream Load的大小在Doris sink里设置max_row_count为50000这样每次导入的数据量有限内存起伏不会太大。4.2 Seatunnel-Web启动报“数据库连接失败”这个坑多半出在初始化元数据库时。Seatunnel-Web的seatunnel_server_mysql.sql会默认使用utf8mb4字符集但是MySQL端如果default_character_set没配好部分表的索引字段会超长。解决办法初始化前在MySQL创建独立库并指定字符集CREATE DATABASE seatunnel_console DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;同时确保MySQL的max_allowed_packet参数足够大否则初始化脚本执行到一半可能因为包太大报错。4.3 MySQL连接被Killserver-id冲突增量任务跑起来之后MySQL端不定期报ERROR 2013 (HY000): Lost connection to MySQL server during query去MySQL查看发现连接被SHUTDOWN或KILL了。查下来是两个Seatunnel任务配了同一个server-id区间。解决办法给每个任务分配唯一且不重叠的server-id范围。另外MySQL参数slave_net_timeout和slave_connect_retry用于控制复制连接的空闲超时Seatunnel的CDC本质上属于伪slave连接也应该把网络超时调大一点比如slave_net_timeout60。4.4 Doris报Column ndv error或“precision loss”全量大表同步时DECIMAL类型出现精度丢失。Doris的DECIMAL(10,2)和MySQL的DECIMAL(10,2)对照看起来一样但Seatunnel的JDBC source读取时把DECIMAL转成JavaBigDecimal再转成string写入Doris中间如果没有显式指定scale可能出现截断。解决办法在Doris的建表语句中显式指定DECIMAL精度。另外在Seatunnel的source查询里可以对具体字段做一次CASTSELECT order_id, user_id, product_code, CAST(pay_amount AS DECIMAL(10,2)) AS pay_amount FROM order_info当然如果你同步的表很少遇到精度问题忽略这个也问题不大。4.5 常见问题速查表故障现象定位方向解决方案全量同步很慢并行度设置过低source并行度调大至4; 确认MySQL连接数上限增量任务不更新binlog解析延迟查看Seatunnel日志, 检查binlog_format是否为ROWDoris目标表无数据数据源连接默认库错误在sink里显式指定table.identifierWeb界面任务一直处于等待引擎资源不足增加Seatunnel集群节点或调大worker线程数同步数据比MySQL少一截同步任务中途崩溃且无checkpoint开启检查点, 设置执行模式为EXACTLY_ONCEDoris Stream Load失败HTTP端口未放行确认FE 8030和BE 8040端口对同步节点开放5. 集成后的运维心得AllData 场景下的调度与监控5.1 同步任务调度策略集成到AllData之后同步任务不再是单独跑一次的作业而是要纳入统一的调度体系。我是这样设计三层调度第一层中台调度入口AllData本身自带调度引擎可以配置每日凌晨2点触发全量同步。第二层Seatunnel-Web内部定时调度间隔5分钟检测增量任务状态如果挂了自动拉起。第三层状态看板通过Seatunnel-Web的API把任务状态、延迟、吞吐量指标接入AllData的监控大盘。提示增量任务不要在中台调度里频繁重启。如果Seatunnel任务因为网络闪断停了直接复用它的自动重试机制即可不要每次都重建任务否则binlog位点丢失会造成重复数据。5.2 关于“先全量后增量”的无缝衔接做MySQL到Doris的同步很多新人的误区是先跑一个全量再去开增量任务结果中间有数据变更没被捕获到出现空窗期。结构化处理方式是用Seatunnel的initial启动模式它会自动先做全量快照再接着开增量中间的binlog不会漏。如果你必须手动分开执行比如全量用别的工具那就在全量完成的时间点记录binlog位点再以该位点启动增量任务。Seatunnel-Web里可以通过“启动模式”指定specific_offset来做到。5.3 同步性能与资源分配经验在AllData中台环境里同步服务往往不是唯一吃资源的应用。给同步服务预留多少资源直接影响全链路稳定性。我这里分享一组实测数据作为参考源表行数全量同步耗时并行度批大小10万行3秒11000180万行40秒420001200万行约5分钟85000注意批大小batch size不要盲目调大。Doris写入侧有单次导入条数限制调太大会触发内存瓶颈。实测1500~5000是比较稳定的区间。6. 写在最后这套集成方案的真实体验这套AllData Seatunnel-Web的MySQL到Doris同步链路我这边已经稳定跑了将近一个月。最大的感受是SeaTunnel确实是综合性价比最好的开源同步框架既有丰富的数据源插件又不需要额外写一行代码去对接APIWeb界面能完成绝大多数运维操作。如果你也在自建数据中台卡在数据同步这块我的建议是优先评估SeaTunnel。它做MySQL到Doris这种链路配置简单程度超过你想象而且天然带Doris的Stream Load优化性能比DataX硬编码要好得多。最后再分享一个小技巧千万别吝啬在测试环境多试几种数据源组合。Seatunnel插件市场里有几十种连接器每一种连接器的配置项都不太一样。我这个项目最初也只打算做MySQL到Doris后来顺手用Seatunnel加了一条MySQL到TDengine的链路几十行JSON就搞定了。中台建设就是这样——底层同步底座搭好了周边需求接水龙头一样随时都能接入。