3. 主流引擎概览:谁在抢增量计算的蛋糕?

增量计算这个赛道,现在玩家不少。我入行那会儿,大家还在用批处理凑合,现在不一样了。Spark Structured Streaming、Flink、Kafka Streams,还有 Doris 和 ClickHouse 的物化视图,以及 Dinky 和 MaxCompute 的增量通道——每个都有自己的脾气。

你想想看,选错了引擎,后面调优就是给自己挖坑。我见过太多团队,因为前期调研不充分,上线后天天加班救火。所以这一章,咱们把这些主流引擎挨个捋一遍。

核心观点: 没有最好的引擎,只有最合适的场景。选型前,先搞清楚你的数据规模、延迟要求、运维能力。
增量计算引擎全景图 实时流处理引擎 Apache Flink Spark Structured Streaming Kafka Streams OLAP 物化视图 Doris 物化视图 ClickHouse 物化视图 增量通道 / 开发框架 MaxCompute 增量通道 Dinky 选型关键维度对比 • 延迟要求:Flink(毫秒级)> Kafka Streams(毫秒级)> Spark Streaming(秒级)> 物化视图(秒~分钟级) • 状态管理:Flink 最强(RocksDB + 分布式快照),Kafka Streams 次之(本地状态 + changelog) • 运维成本:Kafka Streams(低,嵌入应用)< Flink(中)< Spark(中高)< OLAP 物化视图(高,依赖集群) • 数据一致性:Flink(Exactly-Once)> Spark(至少一次/Exactly-Once)> Kafka Streams(Exactly-Once) • 适用场景:实时风控/推荐用 Flink;准实时报表用 Spark;轻量流处理用 Kafka Streams;OLAP 预聚合用物化视图

3.1 Spark Structured Streaming:老牌劲旅的微批哲学

Spark Structured Streaming 本质上还是微批处理。它把流数据切成一个个小批次,然后用 Spark 的批处理引擎去跑。我个人习惯叫它「准实时」——延迟通常在秒级,做不到毫秒。

为什么还有人用?生态好。如果你团队已经有一套 Spark 批处理 pipeline,加个流处理模块几乎零成本。我在项目中遇到过一家电商公司,他们的离线数仓全是 Spark,后来要上实时大屏,直接复用 Spark 集群,省了一大笔运维费。

我的建议: 如果你的延迟要求是 5 秒以上,且团队 Spark 经验丰富,选 Structured Streaming 没错。别硬上 Flink,学习曲线也是成本。

但要注意,Spark 的流处理有个坑——反压机制。它不像 Flink 那样天然支持动态反压。我曾经遇到过一个场景,数据源突然爆发,Spark 的微批堆积,导致 OOM。后来加了限流和背压策略才稳住。

// Spark Structured Streaming 典型配置
spark.conf.set("spark.sql.streaming.schemaInference", "true")
spark.conf.set("spark.sql.streaming.checkpointLocation", "/tmp/checkpoint")
spark.conf.set("spark.sql.streaming.forceDeleteTempCheckpointLocation", "true")

// 反压配置
spark.conf.set("spark.sql.streaming.backpressure.enabled", "true")
spark.conf.set("spark.sql.streaming.backpressure.initialRate", "1000")
spark.conf.set("spark.sql.streaming.backpressure.minRate", "100")
spark.conf.set("spark.sql.streaming.backpressure.maxRate", "5000")

3.2 Apache Flink:实时计算的扛把子

Flink 是真正的流处理引擎。它的事件驱动架构,能做到毫秒级延迟。说白了,数据来了就处理,不等批次。

Flink 最让我佩服的是它的状态管理。RocksDB 作为状态后端,加上分布式快照,容错能力很强。我记得有一次生产环境节点挂了,Flink 自动从 checkpoint 恢复,数据一条没丢。嗯,那一刻我觉得选 Flink 值了。

避坑指南: Flink 的 checkpoint 间隔别设太短。我曾经设成 1 秒一次,结果状态后端频繁刷盘,IO 打满,反而拖慢了处理速度。建议生产环境设 10-30 秒。

Flink 的 SQL 支持也越来越成熟。现在很多场景直接用 Flink SQL 搞定,不用写 Java 代码。但要注意,复杂的多流 join 和窗口聚合,还是得用 DataStream API。

-- Flink SQL 创建流表
CREATE TABLE source_table (
  user_id BIGINT,
  event_time TIMESTAMP(3),
  event_type STRING,
  WATERMARK FOR event_time AS event_time - INTERVAL '5' SECOND
) WITH (
  'connector' = 'kafka',
  'topic' = 'user_events',
  'properties.bootstrap.servers' = 'localhost:9092',
  'format' = 'json'
);

-- 增量聚合查询
SELECT
  TUMBLE_START(event_time, INTERVAL '1' MINUTE) AS window_start,
  event_type,
  COUNT(*) AS event_count
FROM source_table
GROUP BY
  TUMBLE(event_time, INTERVAL '1' MINUTE),
  event_type;

3.3 Kafka Streams:轻量级选手

Kafka Streams 是个异类。它不依赖独立集群,就是一个 Java 库,嵌入到你的应用里。你想想看,部署多简单——打个 jar 包就能跑。

它的核心思想是流表对偶性。KStream 是流,KTable 是表,两者可以互相转换。这个设计很优雅,做实时关联查询特别方便。

但 Kafka Streams 也有短板。状态管理依赖 RocksDB,而且状态大小受限于本地磁盘。我在项目中遇到过状态膨胀到几百 GB 的情况,单机扛不住,最后只能切到 Flink。

适用场景: 轻量级 ETL、实时过滤转换、简单的聚合统计。如果你的数据量不大(每天几亿条以内),Kafka Streams 够用且省心。
// Kafka Streams 拓扑示例
StreamsBuilder builder = new StreamsBuilder();

KStream<String, String> source = builder.stream("input-topic");

KTable<String, Long> counts = source
    .flatMapValues(value -> Arrays.asList(value.toLowerCase().split("\\W+")))
    .groupBy((key, word) -> word)
    .count(Materialized.as("counts-store"));

counts.toStream().to("output-topic", 
    Produced.with(Serdes.String(), Serdes.Long()));

3.4 Doris / ClickHouse 物化视图:OLAP 的增量预聚合

物化视图不是新概念,但在 Doris 和 ClickHouse 里,它被玩出了新花样。说白了,就是预先算好聚合结果,查询时直接读,不用实时算。

Doris 的物化视图支持透明改写。你建好物化视图后,查询 SQL 不用改,Doris 自动路由到最合适的视图。这个特性很实用,我在做实时大屏时,把常用的维度聚合都建了物化视图,查询响应从秒级降到了毫秒级。

ClickHouse 的物化视图更像触发器。数据写入时触发计算,结果存到另一张表。它的AggregatingMergeTree 引擎配合物化视图,做增量聚合非常高效。

注意: 物化视图不是万能的。写放大问题很严重——你写一条数据,可能触发多个物化视图更新。我见过一个案例,建了 10 个物化视图,写入性能直接腰斩。建议控制在 3-5 个以内。
-- Doris 创建物化视图
CREATE MATERIALIZED VIEW mv_user_city_uv
AS
SELECT city, COUNT(DISTINCT user_id) AS uv
FROM user_visit_log
GROUP BY city;

-- ClickHouse 物化视图
CREATE MATERIALIZED VIEW mv_hourly_events
ENGINE = AggregatingMergeTree()
PARTITION BY toYYYYMM(hour)
ORDER BY (event_type, hour)
AS
SELECT
  toStartOfHour(event_time) AS hour,
  event_type,
  countState() AS event_count
FROM source_table
GROUP BY hour, event_type;

3.5 Dinky / MaxCompute 增量通道:国产选手的突围

Dinky 是个 Flink 开发平台,解决了 Flink SQL 开发、调试、运维的痛点。它支持在线 SQL 编辑、任务提交、监控告警。我团队现在就用 Dinky 管理 Flink 任务,确实比裸写 Flink 方便。

MaxCompute 的增量通道,是阿里云提供的 CDC 能力。它支持从 MaxCompute 表实时读取增量数据,配合 Flink 做实时计算。如果你用阿里云,这个组合很香。

我的经验: Dinky 的 checkpoint 管理功能很实用。它会把 checkpoint 路径、状态大小、恢复时间都展示出来,排查问题一目了然。但要注意版本兼容性,Dinky 升级有时会跟 Flink 版本冲突。

3.6 选型决策矩阵

说了这么多,到底怎么选?我整理了一个决策矩阵,你对着自己的场景打勾就行。

场景需求 推荐引擎 理由
毫秒级延迟,复杂状态计算 Flink 事件驱动,状态管理最强
秒级延迟,已有 Spark 生态 Spark Structured Streaming 复用集群,学习成本低
轻量流处理,不想维护集群 Kafka Streams 嵌入应用,部署简单
OLAP 预聚合,加速查询 Doris / ClickHouse 物化视图 透明改写,查询性能极佳
阿里云环境,需要 CDC MaxCompute 增量通道 + Flink 原生集成,运维省心
Flink SQL 开发管理 Dinky 可视化运维,降低门槛
最后说一句: 选型不是一锤子买卖。我建议先做 POC,用真实数据跑一周,看看延迟、吞吐、稳定性。别光看文档,文档里都是理想情况。生产环境会教你做人。