SeaTunnel 实时同步 Kafka JSON 数据到 Oracle 完整配置 + 踩坑实战总结

一、业务场景

实时消费 Kafka Topic 内 JSON 格式消息,通过 JsonPath 解析嵌套 JSON 字段,SQL 做日期格式化、生成主键、新增入库时间,最终批量写入 Oracle 业务表,采用流式任务持续同步。 使用版本:SeaTunnel 2.x(通用 HOCON 配置格式) 数据流链路:Kafka Source(JSON消息) -> JsonPath解析 -> SQL转换加工 -> JDBC Oracle Sink

二、完整可用 conf 配置(Kafka -> Oracle)

文件名:kafka2oracle_stream.conf

bash 复制代码
env {
  # 并行度根据消费速度调整,调试阶段设1方便定位数据
  parallelism = 1
  # 流式持续消费kafka,必须STREAMING
  job.mode = "STREAMING"
}

source {
  Kafka {
    plugin_output = "fuji"
    # kafka消息整体为json格式
    format = json
    topic = "XX"
    bootstrap.servers = "XX"
    consumer.group = "seatunnel_group"
    kafka.config = {
      client.id = "client_1"
      # 单次拉取最大条数,提升同步吞吐量
      max.poll.records = 500
      # 首次启动从头消费历史数据
      auto.offset.reset = "earliest"
      # 自动提交offset,生产可根据业务改为手动提交
      enable.auto.commit = "true"
    }
  }
}

transform {
  # 第一层转换:JsonPath解析消息内content嵌套json字段
  JsonPath {
    plugin_input = "fuji"
    plugin_output = "after_json_parse"
    columns = [
      {
        src_field = "content"
        path = "$.LineName"
        dest_field = "LINENAME"
        dest_type = "string"
      },
      {
        src_field = "content"
        path = "$.MachineName"
        dest_field = "MACHINENAME"
        dest_type = "string"
      },
      {
        src_field = "content"
        path = "$.ModuleNo"
        dest_field = "MODULENO"
        dest_type = "string"
      },
      {
        src_field = "content"
        path = "$.RobotNo"
        dest_field = "ROBOTNO"
        dest_type = "string"
      },
      {
        src_field = "content"
        path = "$.ModuleSerial"
        dest_field = "MODULESERIAL"
        dest_type = "string"
      },
      {
        src_field = "content"
        path = "$.HeadType"
        dest_field = "HEADTYPE"
        dest_type = "string"
      },
      {
        src_field = "content"
        path = "$.HeadSerial"
        dest_field = "HEAFSERIAL"
        dest_type = "string"
      },
      {
        src_field = "content"
        path = "$.UpdateTime"
        dest_field = "UPDATETIME"
        dest_type = "string"
      },
      # 原始完整json存入扩展字段
      {
        src_field = "content"
        path = "$"
        dest_field = "EXTJSON"
        dest_type = "string"
      }
    ]
  }

  # 第二层转换:SQL函数加工、生成主键、日期格式化
  Sql {
    plugin_input = "after_json_parse"
    plugin_output = "result"
    query = """
      SELECT
        UUID() AS ID,
        LINENAME,
        MACHINENAME,
        MODULENO,
        ROBOTNO,
        MODULESERIAL,
        HEADTYPE,
        HEAFSERIAL,
        -- 处理带T的ISO时间字符串,适配Oracle日期类型
        TO_DATE(SUBSTR(UPDATETIME,0,19), 'yyyy-MM-dd''T''HH:mm:ss') AS UPDATETIME,
        NOW() AS INSERTTIME,
        EXTJSON
      FROM after_json_parse
    """
  }
}

sink {
  jdbc {
    plugin_input = "result"
    # Oracle连接串
    url = "jdbc:oracle:thin:xxx:1521:xx"
    driver = "oracle.jdbc.OracleDriver"
    username = "xxx"
    password = "xxx"
    table = "a"
    # 自定义insert语句,字段顺序必须和SQL查询返回顺序严格一致
    query = "INSERT INTO EMS.a(ID,LINENAME,MACHINENAME,MODULENO,ROBOTNO,MODULESERIAL,HEADTYPE,HEAFSERIAL,UPDATETIME,INSERTTIME,EXTJSON) VALUES(?,?,?,?,?,?,?,?,?,?,?)"
  }
}

三、核心配置要点说明

1. Kafka JSON 消息解析:JsonPath src_field 关键配置

Kafka Source format = json 代表整条 kafka 消息体解析为 Json 对象,业务中实际业务字段全部嵌套在content节点下。

  • src_field = "content":指定从解析后的根 json 里取出 content 子对象,再基于这个子对象执行 JsonPath 路径$.LineName提取值;
  • 若不指定src_field,默认从整条消息根节点匹配 path,嵌套结构会提取不到数据。

2. 双层 Transform 设计:JsonPath + SQL 分工

  1. JsonPath:只做 JSON 嵌套字段提取、字段重命名、类型统一;
  2. SQL Transform:负责复杂逻辑,包含:
    • 生成全局唯一主键 UUID ();
    • ISO 格式时间字符串转 Oracle Date 类型;
    • 新增入库时间 INSERTTIME;
    • 统一字段输出顺序,给 JDBC Sink 使用。 拆分后可读性更高,后续新增字段只需要同步修改两处,便于维护。

3. Oracle JDBC Sink 固定规范

  1. 驱动固定:oracle.jdbc.OracleDriver,运行环境必须上传 ojdbc 驱动包到 seatunnel/lib;
  2. 连接串标准格式:jdbc:oracle:thin:@ip:1521:服务名
  3. 自定义 query 写入模式:不依赖框架自动生成 SQL,手写 Insert 语句适配多 schema 表(EMS.a

四、实操踩坑全记录(重点排错)

坑 1:Kafka 能正常消费消息,但 Oracle 无数据入库,无报错日志

现象

任务正常启动,监控看到 kafka offset 持续滚动(数据已经被消费),Oracle 表里无新增数据,控制台无异常堆栈。

根因

使用自定义query参数时,SeaTunnel JDBC Sink 底层仅做参数占位符填充,不会校验:

  1. SQL 查询输出字段数量 和 Insert 问号占位数量是否匹配;
  2. 字段顺序、字段类型和 Oracle 表字段是否对应; 顺序错位、字段数量不一致、类型不兼容不会抛出明显报错,直接丢弃本条数据。
排查步骤
  1. 打印中间流数据:临时增加 Console Sink 输出result流,核对 SQL 查询输出字段顺序;
  2. 逐个数 SELECT 字段数量、Insert 问号数量,保证 1:1 对应;
  3. 核对日期、字符串、UUID 等字段和 Oracle 表字段类型是否匹配。

坑 2:JsonPath 提取不到嵌套 JSON 字段,全部为空

根因

忘记配置src_field = "content",JsonPath 默认从 kafka 消息根节点匹配路径$.LineName,而业务真实字段都在 content 子节点内部。

解决

所有提取嵌套 content 内部字段的 JsonPath 规则,统一增加src_field = "content"

坑 3:ISO 时间字符串2026-06-17T10:30:00写入 Oracle 日期报错

根因

直接将带 T 的字符串存入 Oracle Date 类型,隐式转换失败。

解决

SQL Transform 中截取前 19 位字符,使用 Oracle 兼容格式化模板转换日期:

sql

复制代码
TO_DATE(SUBSTR(UPDATETIME,0,19), 'yyyy-MM-dd''T''HH:mm:ss') AS UPDATETIME

坑 4:Kafka 消费完数据直接丢失,不入库

双重排查方向:

  1. 字段顺序 / 数量不匹配(坑 1 场景),数据静默丢弃;
  2. offset 自动提交时机早于数据库写入完成:
    • 当前配置enable.auto.commit = "true",Kafka 消费者会定时提交 offset,若数据库写入延迟 / 失败,数据已经标记消费,丢失数据;
    • 生产优化:关闭自动提交,开启 JDBC 事务批量写入,写入成功后手动提交 offset。

坑 5:UUID 主键插入 Oracle 报长度 / 格式异常

优化方案
  1. Oracle 主键字段设置为 VARCHAR2 (50),容纳完整 UUID 字符串;
  2. 若业务需要数字主键,可替换 SQL 函数UUID()为雪花 ID 函数。

五、生产优化建议

  1. 并行度:调试用 1,线上根据 Kafka 消息吞吐量调整 parallelism;
  2. Offset 提交:生产环境关闭enable.auto.commit,避免数据丢失;
  3. 批量写入:JDBC Sink 增加 batch.size 参数,提升 Oracle 写入性能;
  4. 日志排查:开启 SeaTunnel DEBUG 日志,打印 Sink 插入前的数据,快速定位字段不匹配问题;
  5. 驱动依赖:部署时确认 ojdbc8 驱动放置在 lib 目录,否则会报驱动类找不到。