3. 主流引擎概览:谁在抢增量计算的蛋糕?
增量计算这个赛道,现在玩家不少。我入行那会儿,大家还在用批处理凑合,现在不一样了。Spark Structured Streaming、Flink、Kafka Streams,还有 Doris 和 ClickHouse 的物化视图,以及 Dinky 和 MaxCompute 的增量通道——每个都有自己的脾气。
你想想看,选错了引擎,后面调优就是给自己挖坑。我见过太多团队,因为前期调研不充分,上线后天天加班救火。所以这一章,咱们把这些主流引擎挨个捋一遍。
3.1 Spark Structured Streaming:老牌劲旅的微批哲学
Spark Structured Streaming 本质上还是微批处理。它把流数据切成一个个小批次,然后用 Spark 的批处理引擎去跑。我个人习惯叫它「准实时」——延迟通常在秒级,做不到毫秒。
为什么还有人用?生态好。如果你团队已经有一套 Spark 批处理 pipeline,加个流处理模块几乎零成本。我在项目中遇到过一家电商公司,他们的离线数仓全是 Spark,后来要上实时大屏,直接复用 Spark 集群,省了一大笔运维费。
但要注意,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 的 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。
// 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 引擎配合物化视图,做增量聚合非常高效。
-- 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 做实时计算。如果你用阿里云,这个组合很香。
3.6 选型决策矩阵
说了这么多,到底怎么选?我整理了一个决策矩阵,你对着自己的场景打勾就行。
| 场景需求 | 推荐引擎 | 理由 |
|---|---|---|
| 毫秒级延迟,复杂状态计算 | Flink | 事件驱动,状态管理最强 |
| 秒级延迟,已有 Spark 生态 | Spark Structured Streaming | 复用集群,学习成本低 |
| 轻量流处理,不想维护集群 | Kafka Streams | 嵌入应用,部署简单 |
| OLAP 预聚合,加速查询 | Doris / ClickHouse 物化视图 | 透明改写,查询性能极佳 |
| 阿里云环境,需要 CDC | MaxCompute 增量通道 + Flink | 原生集成,运维省心 |
| Flink SQL 开发管理 | Dinky | 可视化运维,降低门槛 |