一、引言
实时分析链路的核心,不是把 Kafka、Flink、CDC、StarRocks 这些组件简单串起来,而是把"业务变更、日志事件、指标流"统一成一条可恢复、可追踪、可查询的数据通路。StarRocks 在这条链路中既可以通过Routine Load直接消费 Kafka,也可以通过Flink Connector承接 Flink CDC、实时 ETL 与指标聚合结果,还可以通过Kafka Connector接入 Kafka Connect 生态等。
实时分析链路可以抽象成"采集层、计算层、入仓层、服务层"四段。Kafka 提供削峰、解耦和重放能力;Flink 负责 CDC 读取、维表关联、窗口聚合、清洗转换;StarRocks 负责实时写入后的多维分析和报表查询。

Kafka 不一定是所有数据的最终入口,Flink 也不一定参与所有链路。简单日志流可以直接由 Routine Load 入仓;需要 CDC 语义、跨流关联、聚合计算或复杂格式解析时,Flink 或 Kafka Connect 才是更合适的中间层。
二、Kafka 到 StarRocks
Routine Load 是 StarRocks 内置的 Kafka 消费导入机制,Routine Load 会在 StarRocks 内部持久运行,持续生成一个或多个 load task,消费 Kafka Topic 的全部或部分分区,并将消息导入 StarRocks;Routine Load 支持 exactly-once delivery semantics,避免导入数据丢失或重复。

Routine Load 适合"Kafka 中已有标准化 JSON、CSV、Avro 消息,入仓逻辑相对直接"的场景,它还支持导入时数据转换,并可通过 Primary Key 表承接 UPSERT 和 DELETE。对日志明细、行为事件、服务指标点这类 append-only 数据,Routine Load 的优势是链路短、组件少、运维面小。
一个简化的 Routine Load 示例可以写成:
ini
CREATE ROUTINE LOAD realtime.event_load ON ods_event_log
COLUMNS(event_id, user_id, event_type, event_time, payload)
PROPERTIES
(
"desired_concurrent_number" = "3",
"max_batch_interval" = "10",
"max_batch_rows" = "200000",
"max_error_number" = "1000",
"format" = "json"
)
FROM KAFKA
(
"kafka_broker_list" = "kafka-1:9092,kafka-2:9092",
"kafka_topic" = "app_event_log",
"property.kafka_default_offsets" = "OFFSET_END"
);
- Routine Load 的并发度受
desired_concurrent_number、Kafka 分区数和存活 BE 节点数共同影响,不能只在 StarRocks 侧调参数而忽略 Topic 分区规划。 - 如果 Kafka 中是 Debezium CDC envelope、Protobuf 或多 Topic 复杂映射,Kafka Connector 或 Flink 往往比 Routine Load 更合适。
三、CDC 到 StarRocks
数据库 CDC 链路通常要解决两件事:全量快照和增量 Binlog 的无缝衔接,以及 insert、update、delete 到 OLAP 表模型的正确映射。MySQL 到 StarRocks 的实时同步通过 Flink 分两阶段完成:先同步库表 Schema,再由 Flink 作业同步全量和增量数据;Flink CDC Connector 会先读取历史全量数据,再切换到增量读取,并将数据发送给 StarRocks Flink Connector。

一个 CDC 入仓的 StarRocks 目标表通常应使用 Primary Key 表:
sql
CREATE TABLE dwd_order_realtime (
order_id BIGINT NOT NULL,
dt DATE NOT NULL,
user_id BIGINT,
merchant_id BIGINT,
order_status TINYINT,
pay_amount DECIMAL(18, 2),
update_time DATETIME
)
ENGINE = OLAP
PRIMARY KEY(order_id, dt)
PARTITION BY date_trunc('day', dt)
DISTRIBUTED BY HASH(order_id)
ORDER BY(dt, merchant_id)
PROPERTIES (
"enable_persistent_index" = "true"
);
Primary Key 表适合 CDC 的原因不只是"支持更新",Primary Key 表以 Delete+Insert 策略处理更新,通过主键索引和 DelVector 在写入阶段标记旧版本,查询时只读取同一主键的最新记录,从而避免 Unique Key 表 Merge-On-Read 在查询阶段合并多版本数据。
四、Flink 到 StarRocks
Flink Connector 是实时计算结果入仓的主要路径,Flink Connector 会在内存中积攒小批数据,再通过 Stream Load 一次性导入 StarRocks;它支持 DataStream API、Table API & SQL 和 Python API,并比 Flink 自带 JDBC Connector 更适合持续导入 StarRocks。

在 Flink SQL 中,StarRocks Sink 的核心配置包括connector='starrocks'、jdbc-url、load-url、database-name、table-name、username、password。如果需要 exactly-once,需要将sink.semantic设为exactly-once;sink.version支持V1、V2、AUTO,其中V2使用 Stream Load 事务接口,推荐使用 V2,因为它优化内存使用并提供更稳定的 exactly-once 实现。
ini
CREATE TABLE sr_order_metric (
dt DATE,
merchant_id BIGINT,
pay_order_cnt BIGINT,
pay_amount DECIMAL(18, 2),
update_time TIMESTAMP,
PRIMARY KEY (dt, merchant_id) NOT ENFORCED
) WITH (
'connector' = 'starrocks',
'jdbc-url' = 'jdbc:mysql://fe-1:9030,fe-2:9030',
'load-url' = 'fe-1:8030;fe-2:8030',
'database-name' = 'ads',
'table-name' = 'order_metric_realtime',
'username' = 'flink_writer',
'password' = '******',
'sink.semantic' = 'exactly-once',
'sink.version' = 'AUTO',
'sink.label-prefix' = 'order_metric_job'
);
Flink 写 StarRocks 的参数不是越激进越好,sink.buffer-flush.max-bytes默认 90 MB,增大它可以提升导入性能但可能增加延迟;当sink.semantic为 exactly-once 时,内存数据会在 Flink checkpoint 触发时 flush,这时该参数不生效。因此,指标链路调优不能只看吞吐,还要同时看 checkpoint 间隔、端到端延迟、事务超时和 StarRocks 写入压力。
五、写入语义与一致性
实时链路里常见的三个语义层次是 at-most-once、at-least-once、exactly-once。工程实践中,不能只看某个组件宣称的语义,而要看"源端 offset、计算状态、StarRocks 事务、下游可见性"是否被同一个恢复边界串起来。
sql
Flink Checkpoint
|
| 保存 source offset + operator state + sink label
v
+--------------------+
| StarRocks 事务接口 |
| begin -> load |
| prepare -> commit |
+--------------------+
|
v
数据可见
StarRocks Stream Load 事务接口提供两阶段提交能力,可供 Flink、Kafka 等外部系统导入数据;事务接口包含 begin、load、prepare、commit、rollback 等操作,并继承 label 机制以实现事务去重。 这解释了为什么 Flink Connector 的 exactly-once 需要配合 checkpoint 和事务 Stream Load:Flink 负责在 checkpoint 边界管理状态,StarRocks 负责将同一批写入作为事务提交或回滚。
Routine Load 的一致性边界不同。它是 StarRocks 内部长期运行的 Kafka 消费导入任务,其支持 exactly-once delivery semantics,并会把 load job 拆成多个 load task,每个 task 是一个单独事务,基于 Stream Load 机制导入。 对不需要 Flink 加工的 Kafka 流,Routine Load 可以把消费、offset 与导入事务收敛在 StarRocks 内部,减少外部协调复杂度。
Kafka Connector 的语义则要谨慎看待。 StarRocks Kafka Connector 是 Kafka Connect sink connector,保证 at-least-once 语义;它相比 Routine Load 更适合 Debezium 格式 CDC、自定义转换、多 Topic、Confluent Cloud、以及需要更细粒度控制批大小和并行度的场景。 因此,如果报表必须严格避免重复,Kafka Connector 链路通常要结合 Primary Key 幂等写入、业务主键去重或上游事件唯一键设计。
六、链路选型
一个简单判断准则是:能直接入仓的日志流,用 Routine Load;需要计算的流,用 Flink;已经在 Kafka Connect 生态里的流,用 Kafka Connector;需要承接更新删除的目标表,优先考虑 Primary Key 表。
| 场景 | 推荐链路 | 一致性视角 | 适用原因 |
|---|---|---|---|
| Kafka JSON 日志直接入仓 | Kafka → Routine Load → StarRocks | Routine Load 支持 exactly-once | 链路短,StarRocks 内部管理消费和导入 |
| MySQL CDC 当前态同步 | MySQL → Flink CDC → Flink Connector → Primary Key 表 | MySQL 同步流程支持 exactly-once | 可处理全量 + 增量、更新和删除 |
| Flink 实时指标写入 | Kafka/CDC → Flink → StarRocks | 配置 sink.semantic=exactly-once 并使用事务 Stream Load | 适合窗口聚合、JOIN、清洗转换 |
| Debezium CDC Topic | Kafka Connect → Kafka Connector → Primary Key 表 | Kafka Connector 官方语义为 at-least-once | 适合 Kafka Connect 生态和 Debezium 转换 |
| 多 Topic、多格式数据 | Kafka Connector 或 Flink | 视配置与表模型而定 | 支持更复杂格式、映射和并行控制 |
实时入仓的稳定性往往取决于"小参数"。例如,Flink Connector 的sink.buffer-flush.interval-ms、checkpoint interval、sink.semantic、sink.version会直接影响吞吐、延迟和一致性;Routine Load 的并发度、批大小、错误阈值会影响消费速度和任务稳定性;Kafka Connector 的bufferflush.maxbytes、bufferflush.intervalms和 Kafka Connect 的 offset flush 行为会影响延迟和重复风险。
vbnet
观测指标建议
Kafka:
lag / partition skew / producer error
Flink:
checkpoint duration / backpressure / restart count / sink pending data
StarRocks:
load latency / rejected rows / tablet compaction / BE CPU / query latency
BI:
dashboard freshness / query p95 / metric consistency