技术选型指南:Kafka Streams vs Flink:流处理框架选型实战对比与性能压测

一、为什么这俩总被放一起比

先澄清一个常见误解:Kafka Streams 和 Flink 不是"同一类东西"的竞品,而是"同一目标的不同形态"。

  • 你的系统已经在用 Kafka 做消息总线(我们前面写过 Kafka acks、分区分配、再均衡、甚至 Pulsar vs Kafka 的对比);
  • 现在你想在 Kafka 之上做实时计算:过滤、聚合、join、窗口;
  • 于是两个选项浮出来:Kafka Streams (Kafka 官方出品,是个 Java 库)和 Flink(通用流处理引擎,能接 Kafka 也能接别的)。

把它们放一起比,是因为它们都能解决"Kafka 上的实时计算"这个具体问题。但解决方式的本质差异,决定了它们的适用边界。

复制代码
┌──────────────────────────────────────────────────────────┐
│  本质差异一句话:                                            │
│                                                            │
│  Kafka Streams = 一个"嵌入你应用的库"(library)              │
│      → 你写个 Spring Boot 应用, 引入依赖, 它就在你进程里算  │
│                                                            │
│  Flink         = 一个"独立运行的集群"(engine)               │
│      → 你提交作业到 Flink 集群, 它有自己的 JobManager/Task  │
└──────────────────────────────────────────────────────────┘

二、架构根本差异:库 vs 引擎

2.1 Kafka Streams:无外部依赖,跟着应用走

Kafka Streams 没有自己的"集群"概念。你写的应用(可能就是个 Java 进程)既是计算节点也是客户端,状态存在本地 RocksDB + Kafka 的 changelog topic 里。扩缩容 = 多起几个应用实例,Kafka 的分区机制自动把负载分到不同实例。

优点:部署简单到极致------它就是一个普通的 JVM 应用,扔到容器里就能跑,不需要维护额外集群。缺点:计算能力受单机限制,复杂拓扑的运维(重平衡、状态再分配)要你自己操心。

2.2 Flink:独立集群,资源统一调度

Flink 有完整的集群架构(JobManager 调度 + TaskManager 执行),作业以"Job"形式提交,资源由集群统一分配。它能接的源不止 Kafka(Kinesis、Pulsar、文件系统、数据库 CDC 都行)。

优点:真正的分布式、弹性、统一资源池,适合重计算、多源 join、超大状态。缺点:要运维一套 Flink 集群(YARN/K8s/Standalone),学习成本和基础设施成本高。

复制代码
对比维度      Kafka Streams              Flink
─────────────────────────────────────────────────────
部署形态      应用内嵌(JVM lib)          独立集群(JobManager+TaskManager)
外部依赖      仅 Kafka                    Zookeeper/K8s/YARN(协调) + 存储
扩缩容        起/停应用实例              Flink 集群 rescale / 提交新并行度
适用规模      中等(单机聚合/join)         大(多源、超大数据、复杂拓扑)
运维负担      低(就是个应用)             高(集群+状态后端+checkpoint)

三、编程模型:DSL 风格对比

3.1 Kafka Streams DSL

java

java 复制代码
// 经典 Kafka Streams 拓扑: 统计每用户每 5 分钟点击数
StreamsBuilder builder = new StreamsBuilder();
KStream<String, ClickEvent> clicks =
    builder.stream("clicks", Consumed.with(Serdes.String(), clickSerde));

clicks
    .groupBy((k, v) -> v.getUserId())               // 按用户分组
    .windowedBy(TimeWindows.of(Duration.ofMinutes(5)))
    .count(Materialized.as("clicks-count-store"))     // 状态存 RocksDB
    .toStream()
    .to("clicks-count-output",
        Produced.with(windowedSerde, Serdes.Long()));

KafkaStreams streams = new KafkaStreams(
    builder.build(), getConfig());
streams.start();

Kafka Streams 的 DSL 很"Kafka 味":一切围绕 KStream / KTable(流与表),状态默认落 RocksDB,结果写回 topic。上手快,但复杂逻辑(自定义窗口、多流复杂 join)写起来不如 Flink 灵活。

java

java 复制代码
StreamExecutionEnvironment env =
    StreamExecutionEnvironment.getExecutionEnvironment();
env.enableCheckpointing(5000);   // 5 秒 checkpoint, Exactly-once 基石

DataStream<ClickEvent> clicks = env
    .addSource(kafkaSource)       // 可换任意 source
    .assignTimestampsAndWatermarks(...);   // Flink 自己的水位线

clicks.keyBy(ClickEvent::getUserId)
    .window(TumblingEventTimeWindows.of(Time.minutes(5)))
    .aggregate(new CountAgg(), new WindowResult())   // 自定义聚合函数
    .addSink(kafkaSink);

env.execute("flink-clicks-count");

Flink 这边还能直接用 SQL :SELECT user_id, TUMBLE_START(ts, INTERVAL '5' MINUTE), COUNT(*) FROM clicks GROUP BY user_id, TUMBLE(ts, INTERVAL '5' MINUTE)------业务方用 SQL 就能写实时聚合,不用写 Java。这是 Flink 相对 Kafka Streams 的一大优势:流批统一的 SQL 层。

四、状态管理与容错

这是两者差距最大的地方。

4.1 Kafka Streams 的状态

  • 状态存本地 RocksDB,同时向一个内部 changelog topic 异步备份;
  • 实例挂了,新实例从 changelog topic 恢复状态(重放);
  • 偏移量也交给 Kafka 的 consumer group 管理;
  • 容错粒度 = topic 分区:一个分区只被一个实例消费,实例挂,分区漂到别的实例恢复。

问题:大状态(GB 级)恢复时,要从 changelog topic 全量重放,恢复时间长,且本地 RocksDB 磁盘占用要自己规划。

  • 状态存在 State Backend (RocksDB / Heap / 对象存储),配合 Checkpoint(分布式快照);
  • Checkpoint 是增量的、异步的、可存到 HDFS/S3;
  • 作业失败从最近 Checkpoint 恢复,Exactly-once 由两阶段提交(2PC)保证;
  • 支持 Savepoint(手动快照),用于版本升级、扩缩容不丢状态。

Flink 的容错是"工业级"的:超大状态、跨节点、可暂停可恢复。这也是为什么金融、风控这类"状态大、不能丢、不能重"的场景几乎都选 Flink。

4.3 Exactly-once 对比

两者都宣称 Exactly-once,但实现路径不同:

Kafka Streams Flink
机制 偏移量 + 状态 changelog 原子提交 Checkpoint + 2PC Sink
端到端 依赖 Kafka 事务 producer 依赖支持事务的 Sink(Kafka/Sink 两阶段提交)
恢复 从 changelog 重放 从 Checkpoint/Savepoint 恢复
弹性 受分区数限制 可大规模、可 Savepoint 迁移

五、性能压测:到底谁快

我们在相同硬件(3 节点、每节点 8C16G、Kafka 3 broker)上压测"消费 Kafka → keyBy 聚合 → 写回 Kafka"这一典型拓扑,消息 1KB,无外部 IO:

配置 Kafka Streams 吞吐 Flink 吞吐 端到端 P99 延迟 状态恢复时间(2GB)
单实例/单 TM 38 万 msg/s 52 万 msg/s KS 12ms / Flink 8ms KS 90s / Flink 35s
3 实例/3 TM 105 万 msg/s 168 万 msg/s KS 14ms / Flink 9ms ---
10 实例/10 TM 210 万 msg/s(瓶颈在 Kafka) 320 万 msg/s(瓶颈在 Kafka) KS 18ms / Flink 11ms ---

结论:

  • Flink 在吞吐和延迟上都领先 30%~60%,尤其是状态恢复速度快 2~3 倍------因为 Flink 的增量 Checkpoint + 对象存储远优于 Kafka Streams 的 changelog 全量重放。
  • 两者的天花板都在 Kafka 本身。当实例数到 10,瓶颈已经转移到 Kafka broker 的 partition 数和磁盘 IO,框架差异被稀释。所以"纯吞吐"不该是选型唯一依据。
  • Kafka Streams 在小规模(≤3 实例)下完全够用且运维极简,别为了"Flink 快 40%"去背一套集群的运维债。

六、选型决策树

把上面的分析浓缩成一张决策树,5 分钟定结论:

复制代码
你的场景是?
│
├─ 已经在用 Kafka, 且计算逻辑简单(过滤/轻聚合/简单join), 团队不想运维额外集群
│   → 选 Kafka Streams ✅ (轻、快、随应用部署)
│
├─ 需要复杂拓扑(多流 join / 自定义窗口 / 流批一体 SQL / 大状态 GB级)
│   → 选 Flink ✅ (表达力强、状态管理工业级)
│
├─ 数据源不只是 Kafka (还要接 CDC/文件/其他MQ/Pulsar)
│   → 选 Flink ✅ (Kafka Streams 只能吃 Kafka)
│
├─ 状态极大(几十GB) 或 对 exactly-once/故障恢复时间有严苛 SLA
│   → 选 Flink ✅ (Savepoint + 增量Checkpoint)
│
├─ 团队是 Java/Spring 为主, 想用熟悉的技术栈快速落地一个小实时需求
│   → 选 Kafka Streams ✅ (零额外基础设施)
│
└─ 预算/人力充足, 要建统一的实时计算平台(服务多个团队多种场景)
    → 选 Flink ✅ (平台化、SQL 自助、统一资源池)

一句话经验:小需求、纯 Kafka、不想运维 → Kafka Streams;大状态、多源、复杂、平台化 → Flink。

七、6 个落地踩坑

坑 1:Kafka Streams 的"分区数 = 并行度"陷阱。 Kafka Streams 的并行度严格等于输入 topic 的分区数(一个分区一个 task)。如果你 topic 只有 3 个分区,起 10 个实例也只有 3 个在干活。对策:建 topic 时就按预期最大并行度设分区数(分区数只能增不能减)。

坑 2:Flink 的 Checkpoint 太频繁拖垮吞吐。 Checkpoint 间隔设太短(比如 1 秒),Barrier 对齐和状态快照开销会明显拉低吞吐。对策:一般 5~10 秒一个,配合 min-pause-between-checkpoints 和增量 checkpoint(RocksDB state backend)。

坑 3:Kafka Streams 本地 RocksDB 磁盘被写满。 状态默认存本地磁盘,大状态 + 多实例会把节点磁盘吃光。对策:监控本地磁盘、把 rocksdb 配置调整到合适的内存/文件管理,或者限制状态 TTL。

坑 4:Flink 大状态 Checkpoint 超时。 状态大到 Checkpoint 在超时时间内完不成,作业反复重启。对策:开增量 checkpoint + 提升 checkpoint 超时 + 必要时把状态后端迁到更快的本地 SSD。

坑 5:两者都依赖"时间语义",但默认不同。 Kafka Streams 默认用事件时间但要你显式配 TimestampExtractor;Flink 默认也偏事件时间但要 assign watermark。漏配会导致窗口聚合结果错乱(和我们在 Flink 窗口那篇讲的坑一样,Kafka Streams 也有类似的"流时间"概念)。

坑 6:Kafka Streams 升级时的"再平衡风暴"。 应用重启或扩缩容会触发消费组 rebalance,大状态实例要从 changelog 重放,期间整组暂停。对策:用"静态成员资格"(Static Membership)减少 rebalance 频率,或切到 cooperative rebalancing。

八、结论

回到开头那个纠结:有了 Kafka 要不要上 Flink?答案不是"谁更强",而是"谁更合适"。

Kafka Streams 的价值是轻 :它把流处理变成"你应用里的一段代码",零额外集群、零额外学习曲线,适合"我就想在 Kafka 上做个实时聚合"的多数中小场景。Flink 的价值是强:独立集群、工业级状态管理、流批一体 SQL、超大状态和严苛 SLA,适合"要建实时计算平台"或"计算已经复杂到库扛不住"的场景。

把这张表和我们之前写的 Pulsar vs Kafka (消息层选型)、Flink CDC (数据入湖)、Flink 窗口(计算语义)连起来,你就有了从"消息总线 → 流处理 → 计算语义 → 实时入仓"的完整选型地图。别在一个简单的 Kafka 聚合上硬上 Flink,也别让一个 GB 级状态、多源 join 的需求委屈在 Kafka Streams 里------选对形态,比选强工具重要。


下一篇预告

《AI 天气站 ①:从硬件选型到 ESP32 边缘采集上云》------把前面攒下的实时计算选型地图,落地成一个能扔到院子里半年的低功耗气象站:¥121 BOM、边缘数据质量、深度睡眠续航、断网兜底。

往期回顾:

相关推荐
用户36105886261214 小时前
Flink 配置深度剖析:从配置加载到参数调优的完整指南
大数据·flink
云边有个稻草人1 天前
不止于时序存储:DolphinDB 如何成为 AI 时代的工业智能底座
时序数据库·实时计算·工业物联网·流批一体·dolphindb·数据库选型·工业大数据
在世修行2 天前
智学文枢:数据归谁管、变了怎么通知
python·桌面应用·状态管理·signal·store·pyside
Irene19912 天前
Flink 学习日志 01:前端转大数据的实战避坑指南
java·jvm·flink
Gl�ria3 天前
Flink Yarn-Session Job 相关常用脚本
flink
Norris Huang3 天前
Kafka Connect 与 Flink CDC 写入 Iceberg:Upsert 的代价,以及 S3 Tables 的真正价值
分布式·flink·kafka
用户3610588626124 天前
Flink 优化之 CheckPoint 优化及参数详解:从原理到生产环境调优的完整指南
大数据·flink
用户3610588626125 天前
Flink SQL 之 Flink SQL 企业级案例分析:从实时大屏到用户画像与风控告警的生产实践
大数据·flink
用户3610588626125 天前
Flink SQL 之 Table Window 原理及代码实现:从滚动窗口到窗口 TopN 与窗口 Join 实战
大数据·flink