4. 流批一体技术选型:Flink、Spark Streaming、Kafka Streams 对比与选型策略

做流批一体架构,最绕不开的就是技术选型。我经常被问到:“到底该用 Flink、Spark Streaming 还是 Kafka Streams?” 说实话,没有银弹。每个框架都有自己的脾气,选错了后面改起来很痛苦。今天我就把这三家的底细掰开揉碎,结合我踩过的坑,给你一套可落地的选型策略。

4.1 三大引擎的定位差异

先看一张总览图,帮你快速建立全局认知。

流批一体三大引擎定位对比 Apache Flink 真正的流式运行时 批视为有界流 事件时间 + 精确一次语义 状态管理 + Savepoint SQL/Table API/DataStream Spark Streaming 微批次(Micro-Batch) 批处理生态成熟 Structured Streaming 处理时间为主 MLlib/GraphX 集成 Kafka Streams 轻量级库(非框架) 嵌入应用进程 Kafka 原生集成 无外部依赖 适合事件驱动微服务 核心差异总结 • Flink:流式原生,批流一体最彻底,状态管理最强,延迟最低(毫秒级) • Spark Streaming:微批次架构,批处理生态最丰富,适合与 ETL/ML 混用 • Kafka Streams:库级别,零运维,适合 Kafka 生态内的轻量流处理 选型关键:看你的场景是「全链路流批一体」还是「批为主+流为辅」还是「纯事件驱动」

4.2 核心能力对比

下面这张表我花了不少心思整理,把三个引擎在关键维度上的差异列清楚了。你选型时可以直接拿来当 checklist。

对比维度 Apache Flink Spark Streaming Kafka Streams
运行时模型 真正的流式处理(Dataflow 模型) 微批次(Micro-Batch) 记录级处理(Kafka 消费循环)
延迟 毫秒级 秒级(批次间隔决定) 毫秒级
精确一次语义 端到端支持(Checkpoint + 两阶段提交) 仅输出端(需要幂等或事务) 仅 Kafka 内部(通过事务 API)
状态管理 强大(RocksDB + Heap,支持大状态) 有限(基于 RDD 的 mapWithState) 中等(RocksDB 内置,但受进程内存限制)
事件时间支持 原生支持,Watermark 机制成熟 Structured Streaming 支持,但精度受批次影响 需要手动处理(Kafka 时间戳)
SQL 支持 Flink SQL(流批统一语法) Spark SQL(批为主,流有限) KSQL(轻量,功能有限)
部署运维 需要集群(YARN/K8s/Standalone) 需要集群(YARN/K8s/Standalone) 嵌入应用,无额外运维
生态集成 广泛(Connector 丰富) 非常广泛(Hadoop 生态) 仅 Kafka 生态

我的经验: 如果你需要毫秒级延迟 + 精确一次 + 大状态,Flink 是唯一选择。Spark Streaming 的微批次架构在延迟上天生吃亏,但如果你已经有 Spark 技术栈,用它做流批一体也能接受。

4.3 选型策略:场景驱动

选型不能只看技术参数,得看你的业务场景。我一般按下面三个维度来决策。

4.3.1 场景一:全链路流批一体

说白了,就是同一套代码既要跑实时也要跑离线。比如实时数仓,白天跑实时 ETL,晚上跑全量回溯。

  • 推荐:Flink
  • 理由:Flink 的批流一体最彻底。批就是有界流,API 完全统一。我在做实时数仓时,同一个 Flink SQL 任务,白天消费 Kafka,晚上改成读 Hive,代码一行不改。
  • 避坑:Flink 的批模式在 1.12 之后才成熟,如果你还在用 1.10 以下版本,批性能会差一些。

小技巧: Flink 的 TableEnvironment 可以同时注册 Kafka 表和 Hive 表,通过动态表(Dynamic Table)实现流批统一查询。我习惯把这种模式叫做「一套 SQL 跑天下」。

4.3.2 场景二:批为主,流为辅

你的核心场景是离线批处理,比如每天跑一次全量报表,但偶尔需要接入实时数据做近实时分析。

  • 推荐:Spark Structured Streaming
  • 理由:Spark 的批处理生态太强了。MLlib、GraphX、Spark SQL 都是业界标杆。你只需要把 Spark Streaming 当作批处理的一个补充。
  • 避坑:Structured Streaming 的微批次架构在处理乱序数据时,Watermark 精度不如 Flink。我曾经有个项目,用 Spark 做实时 UV 统计,结果因为批次间隔设置不当,数据偏差了 5%。

注意: 如果你用 Spark Streaming,批次间隔(batch interval)不要设得太小。我见过有人设 100ms,结果集群 CPU 全耗在调度上了。一般建议 1-5 秒。

4.3.3 场景三:轻量级事件驱动

你的场景很简单:从 Kafka 消费数据,做点过滤、转换、聚合,再写回 Kafka。不需要外部存储,不需要大状态。

  • 推荐:Kafka Streams
  • 理由:零运维,嵌入应用,延迟极低。你想想看,一个 Spring Boot 应用里直接引入 Kafka Streams 依赖,就能跑流处理,多省事。
  • 避坑:Kafka Streams 的状态管理受限于进程内存。如果你需要几十 GB 的状态,还是用 Flink 吧。

我的建议: Kafka Streams 最适合做「流式微服务」。比如订单状态流转、风控规则引擎、实时特征计算。我有个项目用 Kafka Streams 做实时推荐特征,延迟不到 10ms,爽得很。

4.4 选型决策树

为了让你更直观地做选择,我画了一张决策树。你顺着走一遍,基本就知道该用哪个了。

流批一体选型决策树 你的核心场景是什么? 全链路流批一体 推荐:Apache Flink 批为主,流为辅 推荐:Spark Structured Streaming 轻量级事件驱动 推荐:Kafka Streams 选型决策说明 路径一:全链路流批一体 → Flink • 同一套代码跑实时和离线 • 需要毫秒级延迟 + 精确一次 + 大状态 • 典型场景:实时数仓、实时风控、实时推荐 路径二:批为主流为辅 → Spark Streaming • 已有 Spark 技术栈,批处理是核心 • 流处理延迟秒级可接受 • 典型场景:离线报表 + 近实时看板、ETL + 流式补充 路径三:轻量事件驱动 → Kafka Streams • 纯 Kafka 生态,无外部依赖 • 状态小,延迟要求极高 • 典型场景:微服务事件编排、实时特征计算、规则引擎

4.5 避坑指南

最后,分享几个我亲身踩过的坑,希望能帮你少走弯路。

坑一:Flink 的 Checkpoint 超时

我曾经有个 Flink 任务,状态 50GB,Checkpoint 总是超时。后来发现是 RocksDB 的并行压缩导致 I/O 打满。解决方案:调大 state.backend.rocksdb.thread.num,或者改用增量 Checkpoint。

坑二:Spark Streaming 的背压问题

Spark Streaming 默认不开启背压(Backpressure)。我有个项目数据量突然暴增,直接把 Kafka 消费 lag 拉到几百万。后来开启了 spark.streaming.backpressure.enabled=true,才稳住。

坑三:Kafka Streams 的分区再均衡

Kafka Streams 在实例重启或扩缩容时,会触发分区再均衡(Rebalance)。如果状态很大,再均衡过程可能很慢。我建议把 num.stream.threads 设大一点,减少实例数量,降低再均衡频率。

4.6 小结

选型没有标准答案,只有最适合你场景的答案。我的经验是:

  • 如果你追求「真正的流批一体」,选 Flink,别犹豫。
  • 如果你已经有 Spark 技术栈,批处理是核心,选 Spark Streaming,够用。
  • 如果你只是做轻量级事件驱动,Kafka Streams 是最优雅的选择。