环境:SeaTunnel 2.3.7(Zeta 引擎)单机集群 + Doris + MySQL CDC
作业:MySQL-CDC → Doris-MultiTableSink,同步
xxxxxx_ihm_database库 28 张表记录时间:2026-10-16
一、环境与关键路径速查
| 项目 | 值 |
|---|---|
| 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"


七、排障方法论(这次学到的)
- 先看原始错误,再猜 :
ErrorURL一条 curl 比猜十次都有用 - 区分三层:数据问题(脏数据)/ 结构问题(表定义)/ 配置问题(超时参数)------解法完全不同,别混
- env 参数看运行值 :配置文件 ≠ 运行中的作业,
running-jobs的envOptions才是真相 - 提交前必查重 :
running-jobs里有同款作业在跑就先停掉,重复提交是万恶之源 - 改完必验证:改配置 → 提交 → 立刻 curl 确认新值生效,闭环才算完成
- 一次排完同类雷:修好一张表,顺手扫全库同类型字段
八、日常运维检查清单
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 分钟起步)。