SeaTunnel + Doris + MySQL-CDC 数据同步故障排查个人笔记

环境:SeaTunnel 2.3.7(Zeta 引擎)单机集群 + Doris + MySQL CDC

作业:MySQL-CDC → Doris-MultiTableSink,同步 xxxxxx_ihm_database 库 28 张表

记录时间:2026-10-16

《通过SeaTunnel 把 MySQL 的数据搬进 Doris》小白学习系列


一、环境与关键路径速查

项目 值
SeaTunnel 安装目录 /data/unimedical/.services/seatunnel
集群服务进程 bin/seatunnel-cluster.sh(Java 主类 SeaTunnelServer)
提交作业客户端 bin/seatunnel.sh --config <配置文件>
服务端日志 logs/seatunnel-engine-server.log(按天滚动,如 .2026-10-15-1)
提交日志 logs/ihm_sync_submit.log(自己指定)
作业配置 business_config/ihm_sync.config
Zeta REST API http://localhost:5801/hazelcast/rest/maps/...
Doris 错误明细 Stream Load 返回的 ErrorURL

牢记两层进程的区别:

  • 集群服务 (SeaTunnelServer,5801 端口):真正跑作业的引擎,长期运行,改配置/改表结构不需要重启它
  • 客户端 (SeaTunnelClient):提交作业的进程,作业提交后可以退出

二、故障时间线(复盘用)

时间 事件
10-15 08:41 作业失败:[DATA_QUALITY_ERROR]too many filtered rows
排查 error log 定位:present_illness 字段 2204 字节 > Doris 限制 2048
修复 Doris 表长文本字段 VARCHAR(2048) → STRING
10-16 14:28/14:30 误操作重复提交两次,两个作业并发打架
14:33/14:35 两个作业先后死于 Checkpoint expired(checkpoint 超时 180 秒太短)
14:41 整体重启集群,重新提交
15:03 新作业再次死于 checkpoint 超时(教训:不改配置重启多少次都没用)
15:07 又一次重复提交,且提交时配置还没改 → 还是旧超时值
15:12 stop 全部旧作业 → 等清空 → 提交 → 验证 checkpoint.timeout=3600000
15:16 作业 1163374879651659777 RUNNING,三个问题全部闭环

三、问题 1:Doris 写入报 too many filtered rows

现象

复制代码
Status: "Fail"
Message: "[DATA_QUALITY_ERROR]too many filtered rows"
NumberTotalRows: 1, NumberFilteredRows: 1

作业直接 FAILED,整条 pipeline 被取消。

排查步骤

第 1 步:拿到被拒数据的具体原因。 日志里 Doris 会返回一个 ErrorURL,在能访问 Doris FE 的机器上执行:

bash 复制代码
curl "http://<doris-fe>:8043/api/_load_error_log?file=__shard_xx/error_log_insert_stmt_xxx"

本次返回:

复制代码
Reason: column_name[present_illness], the length of input is too long than schema.
schema length: 2048; actual length: 2204;

第 2 步:对照源表结构找根因。 SHOW CREATE TABLE 两边对比,发现典型的类型错配:

MySQL VARCHAR(2048) Doris VARCHAR(2048)
单位 字符数 字节数
存满汉字上限 2048 个汉字 约 682 个汉字(2048÷3)

中文病历文本轻松超过 682 个汉字 → 被 Doris 拒绝。

解决方案

长文本字段统一改成 STRING(等价 VARCHAR(65533),约 2 万汉字):

sql 复制代码
ALTER TABLE xxxxxx_ihm_database.ihm_electronic_case
MODIFY COLUMN present_illness STRING NULL COMMENT "现病史",
MODIFY COLUMN chief_complaint  STRING NULL COMMENT "主诉",
MODIFY COLUMN allergy_history  STRING NULL COMMENT "过敏史";
-- ......其他长文本字段同理

注意:

  • 一条语句改多列时,每个字段要带全 NULL/NOT NULL 和 COMMENT,否则定义会被覆盖
  • 扩类型是异步 schema change,用下面命令确认完成后才能重启作业:
sql 复制代码
SHOW ALTER TABLE COLUMN FROM xxxxxx_ihm_database LIKE '%electronic_case%';
-- State 为 FINISHED 才算生效

关于扩容幅度的换算: MySQL VARCHAR(N) 对应 Doris 至少 VARCHAR(3N)(汉字按 3 字节)或干脆 STRING。VARCHAR(4000) 这种"看着大"的值不够安全(4000 字节 < 2048 字符 × 3 = 6144 字节)。存储上不用心疼------Doris 变长列按实际长度压缩存,定义大不占额外空间。

经验

  • 别用 max_filter_ratio 掩盖问题:那是给脏数据准备的,表结构问题跳过 = 永久丢数据
  • 一张表修完,把库里其他表一起扫一遍,一次排完雷:
sql 复制代码
SELECT TABLE_NAME, COLUMN_NAME, CHARACTER_MAXIMUM_LENGTH
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'xxxxxx_ihm_database' AND DATA_TYPE = 'varchar'
ORDER BY CHARACTER_MAXIMUM_LENGTH DESC;

四、问题 2:重复提交 + 作业管理命令

现象

ps -ef | grep seatunnel 出现多个 seatunnel.sh --config ihm_sync.config 客户端 → 集群里两个相同作业并跑,binlog 重复消费、label 冲突。

一个隐蔽的命令坑

bash 复制代码
# 错误写法:& 在重定向前,会被 shell 拆成两条语句,重定向实际没生效
nohup ./bin/seatunnel.sh --config xxx.config & 1>/dev/null 2>&1

# 正确写法:重定向放 & 前面
nohup ./bin/seatunnel.sh --config xxx.config >logs/ihm_sync_submit.log 2>&1 &

另一个坑:相对路径依赖当前目录 。在 logs/ 目录下执行 >logs/xxx.log 会报 No such file or directory。要么先 cd 到安装目录,要么全用绝对路径。

作业管理命令(2.3.7 用 REST API,没有 seatunnel-cli.sh)

bash 复制代码
# 查看所有作业(注意是 running-jobs 复数,少个 s 会报 For input string)
curl -s http://localhost:5801/hazelcast/rest/maps/running-jobs

# 只看关键字段
curl -s http://localhost:5801/hazelcast/rest/maps/running-jobs \
  | grep -oE '"jobId":"[0-9]+"|"jobStatus":"[A-Z]+"|"checkpoint.timeout":"[0-9]+"'

# 停止作业(优雅)
curl -X POST 'http://localhost:5801/hazelcast/rest/maps/stop-job' \
  -H 'Content-Type: application/json' \
  -d '{"jobId": <JOBID>, "isStopWithSavePoint": false}'

# 强制停止(优雅停止卡住时加 "force": true)
# {"jobId": ..., "isStopWithSavePoint": false, "force": true}

集群整体重启(想彻底清干净时用)

bash 复制代码
./bin/stop-seatunnel-cluster.sh     # 停集群,作业全灭
ps -ef | grep seatunnel             # 确认只剩 grep 自己
nohup ./bin/seatunnel-cluster.sh >logs/cluster_restart.out 2>&1 &
ss -ltn | grep 5801                 # 确认端口就绪

正确的提交 SOP(背下来)

bash 复制代码
# 1. 确认配置已改对
grep checkpoint.timeout business_config/ihm_sync.config

# 2. 确认没有残留作业(必须是 [])
curl -s http://localhost:5801/hazelcast/rest/maps/running-jobs

# 3. 提交(一次就够)
cd /data/unimedical/.services/seatunnel
nohup ./bin/seatunnel.sh \
  --config /data/unimedical/.services/seatunnel/business_config/ihm_sync.config \
  >logs/ihm_sync_submit.log 2>&1 &

# 4. 立即验证三要素:一个 jobId + 新配置值 + RUNNING
curl -s http://localhost:5801/hazelcast/rest/maps/running-jobs \
  | grep -oE '"jobId":"[0-9]+"|"checkpoint.timeout":"[0-9]+"|"jobStatus":"[A-Z]+"'

核心原则:改配置 → 停旧作业 → 等清空 → 提交 → 立刻验证。顺序不能乱。


五、问题 3:Checkpoint expired 反复杀作业

现象

作业跑几分钟~十几分钟就挂,日志:

复制代码
timeout checkpoint: <jobId>/1/20, CHECKPOINT_TYPE
CheckpointException: Checkpoint expired before completing.
Please increase checkpoint timeout in the seatunnel.yaml or jobConfig env.
WARN ... checkpoint have error, cancel the pipeline

之后作业被自动从 checkpoint 拉起(指标还在涨,看起来"活着"),但周期性翻车重启。

根因

作业 env 里 checkpoint.interval = 30000、checkpoint.timeout = 180000(3 分钟)。全量快照阶段处理大表大批次数据时,checkpoint barrier 3 分钟传不完 → 超时 → pipeline 被取消。

小表阶段 checkpoint 每 30 秒顺利完成(日志 notify finished),一到大表就炸------所以故障时好时坏、有迷惑性。

解决

ihm_sync.config 的 env 段:

hocon 复制代码
env {
  parallelism = 5
  job.mode = "STREAMING"
  checkpoint.interval = 30000
  checkpoint.timeout = 3600000    # 30 分钟,给全量快照留足余量
}

关键认知:env 参数是提交那一刻定格的! 改配置文件不影响运行中的作业,running-jobs 返回的 envOptions 才是真值------提交后必须 curl 验证。增量阶段 checkpoint 秒级完成,超时设大没有副作用。

排查命令

bash 复制代码
# 看 checkpoint 成败
grep -E "Turn checkpoint_state|Checkpoint expired|checkpoint have error" \
  logs/seatunnel-engine-server.log | tail -10

# 看指定作业的 checkpoint 活动
grep "<JOBID>" logs/seatunnel-engine-server.log | grep -i checkpoint | tail -15

六、日志阅读速成

日志在哪: 启动参数里写着------

复制代码
-Dseatunnel.logs.path=/data/unimedical/.services/seatunnel/logs
-Dseatunnel.logs.file_name=seatunnel-engine-server

常用命令:

bash 复制代码
ps -ef |grep seatunnel

cd /data/unimedical/.services/seatunnel/logs

tail -f seatunnel-engine-server.log                  # 实时跟踪
tail -f seatunnel-engine-server.log | grep -iE "electronic_case|too many filtered|FAILED"
tail -f logs/seatunnel-engine-server.log | grep -iE "checkpoint|too many filtered|FAILED"

less +F seatunnel-engine-server.log                  # F 刷新模式,q 退出
grep -iE "error|exception" seatunnel-engine-server.log | tail -50

常见日志的含义:

日志 含义 需要处理吗
load Result { "Status": "Success" } Doris 写入成功 否
too many filtered rows + ErrorURL 数据被 Doris 拒绝 是,查 ErrorURL
Label Already Exists ... PRECOMMITTED + abort xxx for exist label 作业恢复时清理上次遗留事务 否,正常行为
serverTimezone is obsolete MySQL 驱动参数提示 否
Checkpoint expired before completing checkpoint 超时 是,调大 timeout
datatype restore / Override primary key 启动时同步表结构信息 否

(1)例如实时跟踪关键值日志:

bash 复制代码
tail -f logs/seatunnel-engine-server.log | grep -iE "checkpoint|too many filtered|FAILED"

七、排障方法论(这次学到的)

  1. 先看原始错误,再猜 :ErrorURL 一条 curl 比猜十次都有用
  2. 区分三层:数据问题(脏数据)/ 结构问题(表定义)/ 配置问题(超时参数)------解法完全不同,别混
  3. env 参数看运行值 :配置文件 ≠ 运行中的作业,running-jobs 的 envOptions 才是真相
  4. 提交前必查重 :running-jobs 里有同款作业在跑就先停掉,重复提交是万恶之源
  5. 改完必验证:改配置 → 提交 → 立刻 curl 确认新值生效,闭环才算完成
  6. 一次排完同类雷:修好一张表,顺手扫全库同类型字段

八、日常运维检查清单

bash 复制代码
# 作业健康(一个作业、RUNNING、指标在涨)
curl -s http://localhost:5801/hazelcast/rest/maps/running-jobs | grep -oE '"jobStatus":"[A-Z]+"|"SourceReceivedCount":"[0-9]+"'

# 最近有无报错
grep -iE "error|expired|too many filtered" logs/seatunnel-engine-server.log | tail -10

# 读写计数对比(应基本相等,差值=积压)
# TableSourceReceivedCount vs TableSinkWriteCount

进增量阶段的标志 :SourceReceivedCount 不再暴涨、随业务写入缓慢变化,checkpoint 持续 notify finished。


附:作业恢复/重启速查

场景 做法
只是想重启作业 stop-job → 等清空 → 重新提交(全量重刷)
想从断点续传 ./bin/seatunnel.sh -r <JOBID> -c <配置>(先验证 env 新值是否生效)
集群本身有问题 stop-seatunnel-cluster.sh → seatunnel-cluster.sh
客户端进程残留 直接 kill,不影响集群服务
集群进程挂了 看 seatunnel-engine-server.log 尾部 + 有无 dump 文件(/tmp/seatunnel/dump/zeta-server)

笔记完。三个坑一句话总结:字段扩容用 STRING(按字节算别按字符想)、提交之前先查重(running-jobs 看一眼)、checkpoint 超时要留够(30 分钟起步)。

相关推荐
data analyse 4561 小时前
Session数怎么计算?明确会话切分、超时与跨页规则
java·服务器·数据库
骑着蜗牛撵大象3271 小时前
ToolJet + PostgreSQL:后台表格、查询和流程,一次搭成可远程访问的业务系统
数据库·低代码·docker·postgresql·内部工具·tooljet
先知后行。1 小时前
各种文件系统
linux·服务器·数据库
风若寒892 小时前
MySQL 主从同步,从库中断时间过长,需要主库全量进行覆盖一次
数据库·mysql·主从
l1t2 小时前
duckdb、clickhouse、datafusion、cedardb、tableauhyperapi 5种分析型数据库执行递归求数独SQL比较
数据库·sql·clickhouse
泡海椒2 小时前
JQuick-SQL Worker 生命周期:端口、启动与关闭规范
数据库·sql
麦壳饼2 小时前
SonnetDB:KNN 查询的距离语义与维度边界
数据库·sonnetdb
知行产研2 小时前
三年三起收购,拆解卡特彼勒AI并购版图
数据库·人工智能·mysql