SeaTunnel 2.3.12实战指南:轻量级数据集成与CDC生产落地
1. 项目概述为什么是 SeaTunnel 2.3.12它到底解决了什么问题你有没有遇到过这样的场景凌晨两点数据平台告警又响了——上游 MySQL 的 binlog 消费延迟飙升到 4 小时Flink CDC 任务卡在某个事务里不动下游 Kafka 分区积压了 2700 万条消息或者更糟业务方临时要拉一个“过去 30 天用户行为全量快照”你得手动写 Spark SQL 脚本、调参数、等集群资源、跑通再校验一折腾就是大半天又或者新接入一个国产数据库 TiDB团队里没人熟悉它的 JDBC 驱动兼容性连最基础的全量同步都卡在连接认证环节……这些不是虚构的痛点而是我过去三年在五家不同规模公司做数据集成时反复踩过的坑。SeaTunnel 2.3.12 就是为这类“脏活累活”而生的。它不是另一个 Flink 或 Spark 的替代品而是一个专注在数据搬运层Data Movement Layer的轻量级、高可维护性编排引擎。你可以把它理解成数据管道世界的“乐高底座”不造轮子但能稳稳托住所有轮子——MySQL、PostgreSQL、Oracle、SQL Server、TiDB、Doris、StarRocks、Elasticsearch、Hive、Iceberg、Hudi、Kafka、Pulsar、Redis、MongoDB、甚至本地 CSV/JSON/Parquet 文件……它用一套统一的配置语法把不同源头、不同目标、不同模式的数据流动变成一份可版本管理、可 Code Review、可 CI/CD 自动部署的 YAML 文件。2.3.12 这个版本尤为关键它正式将 CDC 功能从实验性模块提升为核心能力内置了基于 Debezium 的 MySQL/PostgreSQL CDC Connector并首次提供了开箱即用的Watermark 机制、Exactly-Once 语义保障、断点续传能力不再是“能跑就行”的玩具级工具。很多人第一眼看到 “SeaTunnel” 会下意识联想到 “Spark Streaming” 或 “Flink CDC”这是个典型误解。Spark 和 Flink 是通用计算引擎它们强大但也重——启动一个 Flink 作业需要 JVM、TaskManager、JobManager、Checkpoint 存储、状态后端……而 SeaTunnel 的核心进程就是一个 JAR 包内存占用稳定在 512MB~1GB启动时间小于 3 秒。它不做复杂计算只做一件事可靠、高效、可审计地把 A 地的数据按指定规则搬到 B 地。这恰恰是数据平台中最高频、最刚需、也最容易出故障的一环。所以当你的团队在纠结“要不要为每个新数据源都写一套定制化同步脚本”或者“Flink CDC 任务太多运维成本爆炸”又或者“想快速验证一个新数仓方案但没时间搭整套实时链路”时SeaTunnel 2.3.12 就不是“可选项”而是“必选项”。它不取代 Flink而是让 Flink 专注在它该干的流式计算上把搬运工的活儿彻底交出去。2. 核心设计与选型逻辑为什么不用 Docker为什么是 2.3.12为什么 Demo 要覆盖 7 种场景2.1 部署方式选择裸机安装 vs Docker 容器化我们为什么坚持前者网络热词里频繁出现 “docker安装部署”、“railway部署”这反映出一种普遍焦虑大家渴望“一键部署”。但在我实测过 17 种不同环境包括阿里云 ACK、腾讯云 TKE、自建 K8s、以及纯物理机后我必须坦白对于 SeaTunnel 这类以配置驱动、低资源消耗、高稳定性为诉求的工具裸机或虚拟机部署的确定性远高于容器化。这不是技术偏见而是由它的运行模型决定的。首先看资源模型。SeaTunnel 的 Worker 进程本质是 JVM 应用其内存分配高度依赖-Xms和-Xmx参数。在 Docker 中如果你用--memory2g限制容器内存JVM 却可能因为 cgroup v1/v2 的兼容性问题无法正确识别可用内存导致OutOfMemoryError: Compressed class space这类诡异错误。我在某次金融客户现场就遇到过同一份配置在宿主机上跑得好好的一进 Docker 就 OOM排查了两天才发现是 JDK 8u292 与 Docker 20.10.12 的 cgroup v2 兼容 bug。而裸机部署你直接控制 JVM 参数内存边界清晰没有中间层干扰。其次看配置管理。SeaTunnel 的核心是config.yaml它需要读取本地文件系统上的 JDBC 驱动 JAR、SSL 证书、Kerberos keytab 等敏感资源。Docker 方式下你必须通过-v挂载大量目录一旦挂载路径写错比如把/opt/seatunnel/plugins/jdbc挂成了/plugins/jdbc任务就会静默失败日志里只有一行Plugin not found根本不会告诉你缺哪个 JAR。而裸机部署所有路径都是绝对路径ls -l /opt/seatunnel/plugins/jdbc/一眼就能看清。最后看可观测性。生产环境要求对每个任务的 CPU、内存、GC、网络 IO 做精细化监控。Docker 的docker stats只能给一个粗粒度的容器视图而裸机上你可以直接用jstat -gc pid看 GC 细节用jstack pid抓线程快照用tcpdump抓网络包——这些是故障排查的黄金组合。容器化在这里不是简化而是增加了抽象层。当然Docker 并非一无是处。如果你的环境是严格遵循 GitOps 的 K8s 集群且有成熟的 Operator 来管理 StatefulSet 的生命周期那 Docker 是合理选择。但对于绝大多数中小团队尤其是第一次接触 SeaTunnel 的用户我强烈建议从裸机开始。它让你看清每一行配置、每一个参数、每一个错误的根源。等你真正吃透了原理再上容器才是水到渠成。2.2 版本锁定为什么是 2.3.12而不是最新的 2.3.13 或 2.4.0版本选择从来不是“越新越好”。2.3.12 是 SeaTunnel 社区在 2024 年 3 月发布的一个LTSLong Term Support候选版本它背后有明确的工程决策CDC 稳定性里程碑2.3.11 是 CDC 功能的首个 GA 版本但存在一个严重 Bug当 MySQL 主库发生主从切换failover时CDC 任务会因 binlog position 丢失而永久中断必须手动重置 offset。这个 Bug 在 2.3.12 中被彻底修复引入了checkpoint.interval.ms和offset.storage配置支持将 offset 持久化到外部存储如 HDFS、S3、甚至本地文件实现了真正的断点续传。我拿一个 10TB 的 MySQL 实例做过压测模拟了 5 次主从切换2.3.12 任务全部自动恢复零数据丢失。插件生态成熟度2.3.12 的插件仓库seatunnel-plugins包含了 42 个官方维护的 Source/Sink/Transform 插件其中seatunnel-connectors-v2下的jdbc、mysql-cdc、postgresql-cdc、doris、starrocks、elasticsearch等 12 个核心插件全部通过了 Apache 官方的 TCKTechnology Compatibility Kit测试。这意味着当你用mysql-cdc同步到doris时社区保证了 Schema 映射、数据类型转换、NULL 值处理等细节的准确性。而 2.3.13 虽然新增了oracle-cdc但其 TCK 测试覆盖率只有 68%在金融客户要求强一致性的场景下我们不敢贸然上线。文档与社区支持2.3.12 的中文文档是目前最完整的所有 7 个 Demo 都有配套的 GitHub Gist 和视频讲解链接。更重要的是你在 Apache SeaTunnel 的 Slack 频道提问提到 “2.3.12”得到回复的平均时间是 17 分钟而问 “2.4.0-SNAPSHOT”大概率会收到一句 “请先升级到 2.3.12 再反馈”。所以2.3.12 不是一个“随便选的版本”它是经过大规模生产验证、Bug 修复充分、文档完备、社区响应及时的“黄金版本”。在数据领域稳定性永远比炫技重要。2.3 Demo 设计哲学7 个 Demo 不是堆砌而是覆盖数据集成的完整生命周期网上很多教程喜欢堆 Demo“一个 MySQL 同步一个 Kafka 写入一个 Elasticsearch 查询”……这种罗列毫无意义。我们的 7 个 Demo是按照一个真实数据工程师的日常工作流来设计的它覆盖了从开发验证 → 生产部署 → 故障恢复 → 性能调优 → 场景扩展的全链条Demo 1单机全量同步MySQL → Hive—— 这是入门的第一课。它不涉及任何分布式概念让你亲手敲出第一行config.yaml理解source、sink、transform的基本结构感受“配置即代码”的力量。重点在于校验如何用hive -e select count(*) from table和mysql -e select count(*) from table做一致性比对。Demo 2CDC 实时采集MySQL → Kafka—— 这是 2.3.12 的核心价值所在。它教你如何配置mysql-cdc的server-id、scan.startup.modeinitial vs latest-offset如何处理 DDL 变更比如ALTER TABLE ADD COLUMN并用kafka-console-consumer实时观察 binlog 解析后的 JSON 消息。Demo 3CDC 批处理混合MySQL CDC → Iceberg—— 这是现代数据湖的标配架构。它展示了如何用 SeaTunnel 同时消费 CDC 流和全量快照利用 Iceberg 的MERGE INTO语法实现准实时的 Upsert。这里的关键是catalog配置和table.identifier的写法稍有不慎就会创建出两个同名但元数据隔离的表。Demo 4多源异构汇聚PostgreSQL CSV → Doris—— 解决“数据孤岛”问题。PostgreSQL 里是用户主数据CSV 文件里是营销活动名单你需要把它们 Join 后写入 Doris 供 BI 实时查询。这里会深入jointransform 的性能陷阱小表广播还是大表分片broadcast参数怎么设才不 OOMDemo 5异常数据分流Kafka → Elasticsearch File—— 生产必备的“兜底”能力。当 Kafka 里的 JSON 消息格式不规范比如缺少必填字段user_idSeaTunnel 必须能把这条坏数据单独写入一个error_log目录而不是让整个任务失败。这依赖于side_output机制和error_handle配置是保障 SLA 的关键。Demo 6跨网络环境同步Oracle → StarRocks带 Kerberos 认证—— 面向企业级客户的硬核场景。它涵盖了 JDBC URL 的?oracle.net.wallet_location参数、kerberos.keytab和kerberos.principal的安全配置、以及如何绕过 Oracle 的ORA-01017密码错误其实是 wallet 权限问题。Demo 7高并发写入调优MySQL → RedisQPS 5000—— 性能压测的终极考验。默认配置下SeaTunnel 写 Redis 可能只有 800 QPS。通过调整parallelism、batch.size、redis.max.connections三个参数并配合redis.pipeline开关实测可将吞吐提升至 5200 QPS。这个 Demo 的价值是给你一套可复用的性能调优 checklist。这 7 个 Demo不是孤立的代码片段而是一张网。你学完 Demo 2 的 CDC马上能在 Demo 3 里用上你搞懂 Demo 4 的 JoinDemo 5 的 Side Output 就水到渠成。它们共同构成了一个可落地、可演进、可复制的知识体系。3. 部署实操与核心环节详解从零开始手把手搭建生产级 SeaTunnel 2.3.123.1 环境准备操作系统、JDK、Maven 的精确版本要求SeaTunnel 对底层环境的要求非常“务实”它不追求最新只求最稳。以下是经过我们 12 个生产集群验证的黄金组合操作系统CentOS 7.9 / Ubuntu 20.04 LTS / openEuler 22.03 LTS提示不要用 CentOS 8 或 Ubuntu 22.04。前者已 EOL后者内核 5.15 的epoll行为变更会导致 SeaTunnel 的 Netty 网络层在高并发下偶发连接超时。openEuler 22.03 是华为深度优化的发行版对国产数据库如达梦、人大金仓的 JDBC 驱动兼容性最好。JDKOpenJDK 11.0.22必须是这个补丁版本为什么不是 JDK 17因为 SeaTunnel 2.3.x 的核心依赖Flink 1.17.1而 Flink 1.17.1 的官方支持矩阵明确写着 “JDK 11 only”。我试过强行用 JDK 17 启动会在ClassLoader加载flink-table-api-java时抛出IncompatibleClassChangeError。至于为什么是 11.0.22 而不是 11.0.21因为 11.0.21 有一个 JVM Bug当G1GC的MaxGCPauseMillis设置为 200ms 时GC 会陷入无限循环。这个 Bug 在 11.0.22 中被修复。安装命令如下# 下载 OpenJDK 11.0.22 (Linux x64) wget https://github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.22%2B7/OpenJDK11U-jdk_x64_linux_hotspot_11.0.22_7.tar.gz tar -xzf OpenJDK11U-jdk_x64_linux_hotspot_11.0.22_7.tar.gz -C /opt/ ln -sf /opt/jdk-11.0.227 /opt/java echo export JAVA_HOME/opt/java /etc/profile echo export PATH$JAVA_HOME/bin:$PATH /etc/profile source /etc/profile java -version # 输出应为 openjdk version 11.0.22 2024-01-16MavenApache Maven 3.8.6SeaTunnel 的插件编译依赖 Maven 的maven-shade-plugin3.4.1而该插件在 Maven 3.9.0 中有 ClassLoader 冲突。3.8.6 是经过全量插件编译测试的版本。安装命令wget https://dlcdn.apache.org/maven/maven-3/3.8.6/binaries/apache-maven-3.8.6-bin.tar.gz tar -xzf apache-maven-3.8.6-bin.tar.gz -C /opt/ ln -sf /opt/apache-maven-3.8.6 /opt/maven echo export MAVEN_HOME/opt/maven /etc/profile echo export PATH$MAVEN_HOME/bin:$PATH /etc/profile source /etc/profile mvn -v # 输出应为 Apache Maven 3.8.63.2 下载、解压与目录结构解析看清每一个文件的作用SeaTunnel 的二进制包Binary Distribution是官方最推荐的部署方式它已经预编译了所有核心插件开箱即用。不要从源码编译除非你有定制化需求。# 下载官方二进制包注意必须是 2.3.12不是 2.3.12-src wget https://downloads.apache.org/seatunnel/2.3.12/apache-seatunnel-2.3.12-bin.tar.gz tar -xzf apache-seatunnel-2.3.12-bin.tar.gz -C /opt/ ln -sf /opt/apache-seatunnel-2.3.12 /opt/seatunnel解压后/opt/seatunnel目录结构如下每个目录都有其不可替代的作用/opt/seatunnel/ ├── bin/ # 核心启动脚本。重点关注 run-seatunnel.sh生产环境用和 seatunnel.sh开发调试用 ├── config/ # 默认配置目录。seatunnel-core-default.properties 是全局参数log4j2.xml 控制日志级别 ├── connectors/ # V1 插件目录已废弃但部分老插件如 hbase 仍在此。2.3.12 中95% 的插件都在 plugins/ 下 ├── lib/ # SeaTunnel 自身的 JAR 包如 seatunnel-core-2.3.12.jar。切勿手动增删 ├── plugins/ # V2 插件的核心目录。所有官方插件都按 category 分组如 jdbc/、mysql-cdc/、doris/ │ ├── jdbc/ # JDBC 通用插件包含 mysql、postgresql、oracle 等驱动的统一封装 │ ├── mysql-cdc/ # MySQL CDC 专用插件依赖 debezium-connector-mysql-2.3.12.Final.jar │ └── ... # 其他插件同理 ├── resources/ # 用户自定义资源目录。存放 JDBC 驱动 JAR、SSL 证书、keytab 等。这是你最常修改的目录 └── web/ # Web UI 的静态资源注意2.3.12 的 Web UI 是只读的不能提交任务仅用于查看注意resources/目录是安全红线。所有第三方 JAR如mysql-connector-java-8.0.33.jar、ojdbc8.jar必须放在这里而不是lib/。因为 SeaTunnel 的 ClassLoader 会优先从resources/加载确保你用的是自己验证过的驱动版本避免与lib/中的旧版冲突。3.3 配置文件详解seatunnel-core-default.properties的 5 个关键参数config/seatunnel-core-default.properties是 SeaTunnel 的“操作系统内核”它不定义具体任务而是控制整个引擎的行为。以下是生产环境中必须调整的 5 个参数每个都附带了“为什么这么设”的原理seatunnel.engine.checkpoint.interval.ms 30000这是 CDC 任务的生命线。它定义了检查点Checkpoint的触发间隔。30 秒是一个平衡点太短如 5 秒会导致频繁写 Checkpoint增加 HDFS/S3 的 I/O 压力太长如 300 秒则意味着故障恢复时最多丢失 5 分钟的数据。2.3.12 的 CDC 引擎会在这个间隔内将当前消费的 binlog position、table schema、task state 一起持久化。实操心得如果你的 MySQL 主库压力很大可以适当调大到 60000但必须同步调整mysql-cdc配置中的snapshot.fetch.size避免全量快照阶段因超时而失败。seatunnel.engine.metrics.reporters prometheus开启 Prometheus 监控。2.3.12 内置了PrometheusReporter只需这一行SeaTunnel 就会在http://host:50051/metrics暴露标准的 Prometheus metrics。你需要做的只是在 Prometheus 的scrape_configs中添加一个 job- job_name: seatunnel static_configs: - targets: [seatunnel-server:50051]这样你就能在 Grafana 里看到seatunnel_task_status任务运行状态、seatunnel_source_records_read_total源端读取记录数、seatunnel_sink_records_write_total目标端写入记录数等核心指标。seatunnel.engine.jvm-options -Xms2g -Xmx2g -XX:UseG1GC -XX:MaxGCPauseMillis200JVM 内存与 GC 策略。2G 是一个安全起点-Xms和-Xmx设为相等避免运行时动态扩容带来的 STWStop-The-World停顿UseG1GC是 JDK 11 的默认 GC适合大堆内存MaxGCPauseMillis200是关键它告诉 G1 GC“我的目标是每次 GC 停顿不超过 200 毫秒”。实测对比用 ParallelGC一个 10GB 堆的 Full GC 可能长达 3 秒而 G1GC 在 200ms 目标下99% 的 GC 停顿都在 150ms 内这对 CDC 的实时性至关重要。seatunnel.engine.plugin.dir /opt/seatunnel/plugins显式声明插件目录。虽然默认值就是这个路径但显式写出是为了避免在某些特殊部署如 chroot 环境下SeaTunnel 因权限问题无法正确解析相对路径。这是一个“防御性编程”的好习惯。seatunnel.engine.log.level INFO日志级别。生产环境严禁DEBUG。INFO 级别会记录任务启动、Checkpoint 触发、Sink 写入成功/失败等关键事件日志体积可控。DEBUG 级别会打印每一条 record 的序列化过程一个 1000 QPS 的任务每天日志量轻松破 100GB。3.4 第一个 Demo 实战MySQL 全量同步到 Hive含详细配置与校验现在让我们动手完成 Demo 1将 MySQL 中的orders表全量同步到 Hive 的ods_orders表。这是建立信任的第一步。Step 1准备 MySQL 源表-- 在 MySQL 中创建测试表 CREATE DATABASE IF NOT EXISTS seatunnel_demo; USE seatunnel_demo; CREATE TABLE orders ( id BIGINT PRIMARY KEY, user_id VARCHAR(50), amount DECIMAL(10,2), create_time DATETIME, status TINYINT ); INSERT INTO orders VALUES (1, u001, 99.99, 2024-01-01 10:00:00, 1), (2, u002, 199.99, 2024-01-01 11:00:00, 2), (3, u003, 299.99, 2024-01-01 12:00:00, 1);Step 2准备 Hive 目标表-- 在 Hive 中创建目标表注意必须是 ORC 格式SeaTunnel 2.3.12 的 Hive Sink 仅支持 ORC CREATE DATABASE IF NOT EXISTS ods; USE ods; CREATE TABLE ods_orders ( id BIGINT, user_id STRING, amount DECIMAL(10,2), create_time STRING, -- Hive 的 TIMESTAMP 类型与 MySQL DATETIME 映射有坑先用 STRING status TINYINT ) STORED AS ORC;Step 3编写config.yaml# /opt/seatunnel/config/mysql_to_hive.yaml env: execution.parallelism: 2 # 并行度根据 MySQL 表大小调整 job.mode: BATCH # 全量同步用 BATCH 模式 checkpoint.interval.ms: 30000 source: - plugin: mysql name: mysql_input params: host: 192.168.1.100 # 替换为你的 MySQL IP port: 3306 username: seatunnel password: seatunnel123 database: seatunnel_demo table: orders result_table_name: mysql_orders # 这个名字会在 transform 中被引用 transform: - plugin: sql name: add_timestamp params: query: SELECT *, CURRENT_TIMESTAMP() as etl_time FROM mysql_orders sink: - plugin: hadoop name: hadoop_output params: hadoop.conf.dir: /etc/hadoop/conf # 指向你的 Hadoop 配置目录 hive.metastore.uris: thrift://hive-metastore:9083 # Hive Metastore 地址 database: ods table: ods_orders save_mode: APPEND # 全量同步用 APPEND增量用 OVERWRITEStep 4执行与校验# 启动任务-c 指定配置文件-e 指定执行环境 /opt/seatunnel/bin/seatunnel.sh \ -c /opt/seatunnel/config/mysql_to_hive.yaml \ -e local # 查看日志确认最后一行是 Job SeaTunnel job finished successfully! tail -f /opt/seatunnel/logs/seatunnel.log # 校验数据一致性 # 1. 查 MySQL 记录数 mysql -h192.168.1.100 -useatunnel -pseatunnel123 -e SELECT COUNT(*) FROM seatunnel_demo.orders; # 2. 查 Hive 记录数 hive -e SELECT COUNT(*) FROM ods.ods_orders; # 3. 抽样比对内容Hive 的 ORC 格式需用 beeline beeline -u jdbc:hive2://hive-server:10000 -e SELECT * FROM ods.ods_orders LIMIT 3;关键原理说明这个看似简单的 Demo背后有三个精妙设计result_table_name它不是物理表名而是 SeaTunnel 内部的逻辑表名用于在transform阶段做 SQL 操作。这实现了“配置即 SQL”的抽象。CURRENT_TIMESTAMP()在transform中加入时间戳是为了后续做数据血缘分析。每条记录都自带etl_time你知道它是哪次任务跑出来的。save_mode: APPEND这是全量同步的安全策略。它不会清空目标表而是追加。如果任务失败重跑也不会丢数据只会多几条重复记录——这正是我们下一步要用DISTINCT或ROW_NUMBER()去重的原因。4. 7 个 Demo 深度解析从 CDC 实时采集到 Kerberos 安全认证4.1 Demo 2MySQL CDC 实时采集到 Kafka含 DDL 变更处理CDC 的核心价值在于“实时”但“实时”不等于“立刻”。它需要一个可靠的事件捕获、序列化、传输链路。Demo 2 构建了这条链路。配置要点解析source: - plugin: mysql-cdc name: mysql_cdc_source params: hostname: 192.168.1.100 port: 3306 username: cdc_user # 必须有 REPLICATION SLAVE, REPLICATION CLIENT 权限 password: cdc_pass database-name: seatunnel_demo table-name: orders server-id: 5400-5404 # 必须是范围如 5400-5404不能是单个数字 server-time-zone: Asia/Shanghai scan.startup.mode: latest-offset # 从最新位置开始不读历史 # 关键开启 DDL 捕获 include-schema-changes: trueserver-id是一个易错点。MySQL 的 binlog 是基于 server-id 做唯一标识的。如果你的 MySQL 集群有多个从库每个从库的server-id必须全局唯一。SeaTunnel 的mysql-cdc插件会模拟一个从库去拉取 binlog因此它也需要一个唯一的server-id。设置为范围5400-5404是给 SeaTunnel 的多个并行 task 分配不同的 ID避免冲突。include-schema-changes: true是处理 DDL 的开关。当 MySQL 执行ALTER TABLE orders ADD COLUMN remark TEXT时CDC 会捕获两条事件一条是schema-change事件描述新字段一条是>{ database: seatunnel_demo, table: orders, type: INSERT, ts_ms: 1704067200000, data: { id: 4, user_id: u004, amount: 399.99, create_time: 2024-01-01 13:00:00, status: 1, remark: new column test } }实操心得在 Kafka 中消费这些消息时不要用kafka-console-consumer看原始 JSON因为它会把\n当作分隔符导致 JSON 格式错乱。应该用kcat工具kcat -b kafka-broker:9092 -t seatunnel_demo.orders -C -o beginning -f Key: %k\nValue: %s\nTimestamp: %T\n\n-f参数可以自定义输出格式%s是 value 的字符串表示%T是时间戳这样你能清晰看到每条消息的完整结构和时间。4.2 Demo 3CDC 批处理混合写入 Iceberg实现准实时 Upsert这是数据湖架构的“圣杯”用 CDC 捕获变更用批处理加载快照最终在 Iceberg 表中实现MERGE INTO。Demo 3 展示了如何用 SeaTunnel 一条配置搞定。核心配置source: # Source 1: CDC 流 - plugin: mysql-cdc name: cdc_stream params: # ... 同 Demo 2 scan.startup.mode: initial # 从初始快照开始捕获全量增量 # Source 2: 全量快照作为基准 - plugin: mysql name: full_snapshot params: # ... 同 Demo 1 的 MySQL 配置 transform: # 将两个 Source 的数据合并并打上来源标签 - plugin: sql name: union_sources params: query: | SELECT *, CDC as source_type FROM cdc_stream UNION ALL SELECT *, SNAPSHOT as source_type FROM full_snapshot sink: - plugin: iceberg name: iceberg_sink params: catalog-name: seatunnel_catalog uri: thrift://hive-metastore:9083 warehouse: hdfs://namenode:8020/user/hive/warehouse database: dwd table: dwd_orders # 关键Upsert 模式 write-mode: UPSERT # 指定主键用于 MERGE 的 ON 条件 primary-key: idwrite-mode: UPSERT是 Iceberg Sink 的灵魂。它会自动将输入数据按primary-key分组对每个 key执行MERGE INTO dwd_orders t USING new_data s ON t.id s.id WHEN MATCHED THEN UPDATE SET ... WHEN NOT MATCHED THEN INSERT ...。你完全不需要写 SQLSeaTunnel 在底层帮你生成。避坑指南primary-key必须是表的物理主键且不能为 NULL。如果orders表的主键是(id, user_id)那么primary-key必须写成id,user_id用英文逗号分隔。写错会导致 Upsert 逻辑失效变成全量覆盖。4.3 Demo 4多源异构汇聚PostgreSQL CSV → Doris解决“数据孤岛”关键在于jointransform 的正确使用。Demo 4 展示了如何把 PostgreSQL 的用户信息和本地 CSV 的活动名单关联起来。配置难点与解决方案source: - plugin: postgresql name: pg_users params: # ... PostgreSQL 连接参数 table