TOS-CDC SQL Connector 是流式计算 Flink 版提供的源表(Source)连接器,用于持续获取火山引擎对象存储 TOS 中的对象元数据。连接器可先扫描指定路径下的已有对象,再从 Kafka 接收对象创建、删除等事件,将全量快照与增量变化组合为一张持续更新的 Flink SQL 表。
类别 | 详情 |
|---|---|
支持类型 | Source |
作业类型 | Flink SQL |
运行模式 | 流模式 |
支持的变更类型 |
|
组件形态 | 平台内置,无需独立部署 |
交付语义 | At-Least-Once |
组件 | 支持版本 | 说明 |
|---|---|---|
火山引擎流式计算 Flink 版 |
| Connector 已由平台内置,无需上传额外 JAR。 |
火山引擎对象存储 TOS | 暂无版本要求。 |
initial 模式先扫描指定 TOS 路径下的已有对象,再从全量扫描开始时间对应的 Kafka 位点持续消费对象事件,用于构建并维护对象元数据状态。initial、snapshot、earliest-offset、latest-offset、group-offsets、timestamp 和 specific-offsets 七种启动模式,可以按需执行全量加增量同步、仅快照读取、增量续读或历史事件回放。initial 模式支持通过 rewind.offset 将 Kafka 消费起点回拨到全量扫描开始时间之前,降低全量扫描期间对象事件遗漏的风险;同时支持配置 Kafka 历史消息过期时的处理策略。event.fail-on-out-of-scope 后,可在发现范围外事件时使任务失败,帮助及时发现 Topic 或数据源配置错误。record_origin 区分 SNAPSHOT 和 EVENT 来源,便于下游统一处理和排查重复数据。object_key 声明为逻辑主键,并配合支持 INSERT、DELETE Changelog 的下游 Sink,维护 Bucket 中对象的当前元数据状态。tos:ObjectCreated:*;如需同步对象删除,还应订阅 tos:ObjectRemoved:*。事件通知规则的 Prefix、Suffix 应覆盖 Source 配置的对象路径。详细操作请参见设置事件通知推送至 Kafka限制项 | 说明 |
|---|---|
全量扫描范围 |
|
增量事件范围 | 默认过滤 Kafka Topic 中不属于当前 Bucket 或配置路径范围的事件。设置 |
变更日志语义 | 全量快照和增量 PUT 事件输出 |
数据一致性 | 当前采用至少一次(At-Least-Once)语义。故障恢复或通过 |
使用 CREATE TABLE 语句创建 TOS-CDC 源表。字段可以按任意顺序声明,也可以只声明需要使用的字段,但必须包含 object_key字段,且必须为STRING NOT NULL。
CREATE TABLE <source_table> ( object_key STRING NOT NULL, object_url STRING, bucket_name STRING, file_name STRING, object_etag STRING, object_size BIGINT, mtime TIMESTAMP_LTZ(3), event_time TIMESTAMP_LTZ(3), record_origin STRING -- PRIMARY KEY (object_key) NOT ENFORCED -- 可选 ) WITH ( 'connector' = 'tos-cdc', 'path' = 'tos://<bucket>/<path>', 'scan.startup.mode' = '<startup_mode>', 'topic' = '<kafka_topic>', 'properties.bootstrap.servers' = '<kafka_broker>', 'properties.group.id' = '<consumer_group>' );
TOS-CDC SQL Connector 使用固定的对象元数据 Schema。
DDL 可声明下表中的任意字段子集,但 object_key 必须声明为 STRING NOT NULL。
字段 | Flink SQL 类型 | 是否必需 | 说明 |
|---|---|---|---|
|
| 是 | 对象在 Bucket 中的原始 Key,可在单 Bucket 内唯一标识对象。 |
|
| 否 | 根据 Bucket 和 Key 生成的完整 TOS URL,格式为 |
|
| 否 | TOS Bucket 名称。 |
|
| 否 | 从 |
|
| 否 | 全量快照或增量事件提供的 ETag;部分事件可能不包含该值。 |
|
| 否 | 全量快照或增量事件提供的对象大小,单位为字节。 |
|
| 否 | 全量快照中的对象修改时间;增量事件中通常为空。 |
|
| 否 | 增量事件的发生时间;全量快照阶段为空。 |
|
| 否 | 记录来源。 |
说明
“无默认值”表示连接器不会自动填充该参数。参数是否必填取决于对应的启动模式,详情参见”生效模式“和”说明“列。
参数 | 类型 | 默认值 | 生效模式 | 说明 |
|---|---|---|---|---|
| STRING | 无 | 全部 | 必填。固定取值为 |
| STRING | 无 | 全部 | 必填。一个或多个 TOS URI,格式为 |
| STRING |
| 全部 | 启动模式。各取值的运行方式和必填参数请参见启动模式。 |
| BIGINT | 无 |
| 必填。非负 Unix 时间戳,单位为毫秒;其他模式忽略该参数。 |
| STRING | 无 |
| 必填。格式为 |
| STRING | 无 | 除 | 必填。用于接收 TOS 对象事件通知的 Kafka Topic。 |
| STRING | 无 | 除 | 必填。Kafka Broker 地址。 |
| STRING | 无 | 增量阶段 |
|
| STRING | 无 | 增量阶段 | 透传给底层 Kafka Consumer 的参数。 |
| INT |
|
| 全量快照阶段的并发度上限。 |
| INT |
| 除 | 增量 Kafka 阶段的最大活跃 Reader 数。 |
| INT |
|
| 单个 LIST Split 允许包含的最大对象数。超过该值时作业失败,以避免单个 Split 过大引发内存风险。 |
| STRING | 无 |
| 全量快照阶段的文件后缀过滤条件,例如 |
| DURATION |
|
| 增量阶段从 |
| STRING |
|
| 回拨目标早于 Kafka 最早保留消息时的处理策略。 |
| BOOLEAN |
| 除 | 是否在收到范围外 Kafka 对象事件时使作业失败。默认过滤范围外事件。 |
| STRING |
| 全部 | 为生成的 |
以下是针对启动模式参数scan.startup.mode 的说明:
取值 | 额外必填参数 | 运行方式 | 适用场景 |
|---|---|---|---|
|
| 先读取 TOS 路径下的已有对象,再切换到 Kafka 持续读取对象事件。 | 首次构建完整对象状态并持续同步。 |
| 无 | 仅读取 TOS 快照;读取完成后 Source 结束。 | 一次性获取对象清单快照。 |
|
| 跳过快照,从 Kafka 当前可用的最早 Offset 开始读取。 | 读取当前仍保留的全部增量事件。 |
|
| 跳过快照,从 Kafka 当前末尾开始读取。 | 只消费作业启动后新增的事件。 |
|
| 跳过快照,从 Consumer Group 已提交的 Offset 开始读取。 | 延续已有 Consumer Group 的消费进度。 |
|
| 跳过快照,从指定时间戳对应的 Kafka Offset 开始读取。 | 从指定时间开始消费。 |
|
| 跳过快照,从指定的 Kafka Partition Offset 开始读取。 | 精确控制各 Partition 的消费起点。 |
所有以 properties. 开头的参数都会去掉该前缀后透传给底层 Kafka Consumer。例如:
DDL 参数 | Kafka Consumer 参数 |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
CREATE TABLE object_meta ( object_key STRING NOT NULL ) WITH ( 'connector' = 'tos-cdc', 'path' = 'tos://my-bucket/ingest/', 'scan.startup.mode' = 'initial', 'topic' = 'object-events', 'properties.bootstrap.servers' = 'kafka.example:9092', 'properties.group.id' = 'ingest-cg', 'properties.security.protocol' = 'SASL_SSL', 'properties.sasl.mechanism' = 'PLAIN', 'properties.sasl.jaas.config' = 'org.apache.kafka.common.security.plain.PlainLoginModule required username="<username>" password="<password>";', 'properties.max.poll.records' = '500' );
以下示例使用 initial 模式读取指定 Bucket 中两个路径下的已有对象,并持续消费 Kafka 中的对象事件。示例将变更日志写入 Print Connector 来查看输出结果。
CREATE TABLE object_changes ( object_key STRING NOT NULL, object_url STRING, bucket_name STRING, file_name STRING, object_etag STRING, object_size BIGINT, mtime TIMESTAMP_LTZ(3), event_time TIMESTAMP_LTZ(3), record_origin STRING, PRIMARY KEY (object_key) NOT ENFORCED ) WITH ( 'connector' = 'tos-cdc', 'path' = 'tos://my-bucket/images01/, tos://my-bucket/images02/', 'properties.bootstrap.servers' = 'kafka.example:9092', 'properties.group.id' = 'ingest-cg', 'topic' = 'object-events', 'scan.startup.mode' = 'initial' ); CREATE TABLE object_state ( object_key STRING NOT NULL, object_url STRING, bucket_name STRING, file_name STRING, object_etag STRING, object_size BIGINT, mtime TIMESTAMP_LTZ(3), event_time TIMESTAMP_LTZ(3), record_origin STRING ) WITH ( 'connector' = 'print' ); INSERT INTO object_state SELECT object_key, object_url, bucket_name, file_name, object_etag, object_size, mtime, event_time, record_origin FROM object_changes;
输出结果示例:
1> +I[full-data/images/test09.jpg, tos://zx-source-test/full-data/images/test09.jpg, zx-source-test, test09.jpg, null, 16673, 2026-07-27T11:43:35Z, null, SNAPSHOT] 2> +I[full-data/images/test14.jpg, tos://zx-source-test/full-data/images/test14.jpg, zx-source-test, test14.jpg, "5b31d1c97b76087ba6129148a9fdd3b1", 517, null, 2026-07-27T18:01:45Z, EVENT] 1> +I[full-data/images/test13.jpg, tos://zx-source-test/full-data/images/test13.jpg, zx-source-test, test13.jpg, "5b31d1c97b76087ba6129148a9fdd3b1", 517, null, 2026-07-27T18:01:45Z, EVENT] 1> -D[full-data/images/test13.jpg, tos://zx-source-test/full-data/images/test13.jpg, zx-source-test, test13.jpg, "5b31d1c97b76087ba6129148a9fdd3b1", 517, null, 2026-07-27T18:02:00Z, EVENT] 1> -D[full-data/images/test06.jpg, tos://zx-source-test/full-data/images/test06.jpg, zx-source-test, test06.jpg, "ef4bc467b27e4bc95a7d23f534ae95b8", 427, null, 2026-07-27T18:02:00Z, EVENT]
说明
+I 表示插入记录,-D 表示删除记录;record_origin 可用于区分全量快照和增量事件。
在 initial 模式下,连接器启动时记录时间 T_snapshot_start,然后对 path 指定的对象范围执行全量 LIST。全量阶段结束后,连接器从 Kafka Topic 中 T_snapshot_start - rewind.offset 对应的位置开始消费,避免遗漏全量扫描期间产生的对象事件。
生产环境建议将 rewind.retention-miss-policy 保持为默认值 fail。当回拨目标早于 Kafka 最早保留消息时,作业会失败,避免在没有提示的情况下丢失事件。只有在明确接受历史事件丢失风险时,才设置为 earliest。
全量快照记录和对象 PUT 事件输出 RowKind.INSERT,对象删除事件输出 RowKind.DELETE。连接器不输出 UPDATE_BEFORE 或 UPDATE_AFTER。
若需要将删除事件同步到结果表,下游 Sink 必须支持 Flink DELETE 变更日志,并配置能够唯一标识对象的主键。单 Bucket 场景可使用 object_key;将多个 Bucket 汇总到同一结果表时,建议使用 object_url。
record_origin = SNAPSHOT 的 +I 记录。record_origin = EVENT 的 +I 记录。-D 记录。问题 | 处理建议 |
|---|---|
全量结束后切换增量阶段失败 | 检查 Kafka 消息保留时间是否覆盖全量扫描耗时和 |
结果表中的记录未随 TOS 对象删除 | 确认下游 Sink 支持 Flink DELETE 变更日志,并在结果表中配置了可唯一标识对象的主键。单Bucket场景可使用 |
故障恢复后出现重复记录 | 连接器采用至少一次语义,恢复时可能重新处理上次 Checkpoint 后的数据。下游应结合业务主键和对象元数据构造去重键。 |
收到不属于配置范围的对象事件 | 默认情况下连接器会过滤范围外事件。若希望立即暴露事件通知配置问题,可设置 |
| 检查 Kafka 消息时间戳是否准确反映消息写入时间。若存在以下情况,建议设置正数
|
initial 模式前,应根据 Bucket 对象规模评估全量扫描耗时,并为 Kafka Topic 配置足够的消息保留时间。rewind.offset 会扩大重复消费的时间窗口。增大该值前,应确认下游具备符合业务语义的去重能力。