Flink 的对手换了:三条分野,重排实时计算选型

简介: 本文深度剖析2026年Flink与Spark等流处理方案的本质差异,摒弃过时对比表,聚焦**执行模型、状态归属、交付形态**三大分野,直击选型核心:不是“谁更强”,而是“哪种复杂度你付得起”。

「Flink 和 Spark 有什么区别?」这个问题,我在面试和选型会上被问过不下二十次。

前几年的标准答案是:Flink 是真流,来一条处理一条;Spark Streaming 是微批,攒一小批再算,所以 Flink 延迟更低。这个答案在 2018 年成立。

到了 2026 年,它至少有三处不准:Spark 的 Real-Time Mode 已经在 4.1 里 GA,把无状态链路压进了毫秒;Kafka Streams 的精确一次早就不是新闻;而真正在抢 Flink 场景的,已经不是 Spark,是一批把状态直接放进对象存储的流式数据库。

更麻烦的是,很多团队手里的对比表还是五六年前那张:Storm、Spark Streaming、Structured Streaming、Flink 四列,比模型、比 API、比 SQL 支持、比延迟。表本身没错,只是其中一半的格子已经过期了。

这篇想把这张表重做一遍。不用「八个维度打分」的方式,而是拆成三条分野——执行模型、状态归属、交付形态。三条问清楚,选型基本就定了。

一、先看牌桌上还剩谁

在这里插入图片描述

今天还在被选的方案,按交付形态能分成四类:

计算引擎是 Flink 和 Spark Structured Streaming。你运维一个集群、一套作业平台,作业是提交上去的长期进程。

嵌入式库只有 Kafka Streams 一个能打。它不是集群,是一个 jar 包,塞进你的 Spring Boot 服务里,服务启动就开始算。

流式数据库是 RisingWave、Materialize 这一批。对外它就是个 PostgreSQL,你连上去写 CREATE MATERIALIZED VIEW,它自己维护新鲜度。状态不在本地盘,在对象存储。

维护态引擎是 Storm。2026 年 7 月它发了 3.0.0,要求 Java 25,API 与 2.x 兼容,分了 lite 和 full 两个发行版——但 PMC 在给 ASF 董事会的报告里写得很直白:项目处于维护状态,活跃成员接近法定人数下限。新项目不必再考虑它,存量系统还得养着。

四条路线的差别,本质是复杂度放在了哪一层:放进引擎,你要养平台;放进你的进程,你要守纪律;放进存储,你要接受一个新生态。

顺带说一句 Storm 为什么退场。它的模型并不落后——record-at-a-time,延迟很低;问题出在容错。Storm 用 acker 给每条 tuple 记一本账:spout 发出去一个根 tuple,每派生一个子 tuple 就异或一次,全链路处理完账本归零才算成功,超时则重发。这套机制能做到 at-least-once,但不维护状态——中间结果得你自己塞进 Redis 或 HBase。后来 Trident 用微批补上了状态和 exactly-once,可那时 Flink 已经把「状态 + 快照」做成了引擎的内置能力。Storm 输的不是延迟,是它把最难的那部分留给了业务代码。

二、分野一:执行模型

在这里插入图片描述

这是最有共识、也最容易被说错的一格。

Flink 是原生流:记录一条一条穿过算子链,算子之间不落地,靠队列传递。Structured Streaming 默认是微批:把流切成一个个小批次,每批走一次「规划 → 调度 → 提交」。

关键在那个空档。每一批开始时,Driver 都要重新规划一次物理计划、向集群申请一次资源、把任务发下去,批结束时再提交一次 offset。这段开销和数据量无关——哪怕一批只有十条记录,它也得走完。所以微批的延迟地板大约是 100 毫秒,卡住它的不是处理能力,是调度单位。

Spark 4.x 的 Real-Time Mode 动的正是这里。它把「每批一组短命任务」换成「每个输入分区一个长驻任务」:任务不再随批次起停,记录到了就往下走;offset 也不在批开始前定好,而是任务运行时按时间预算读多少算多少,结束后再上报。批长于是变成了提交间隔,不再等于延迟。

// 微批:批长就是延迟
.trigger(Trigger.ProcessingTime("10 seconds"))

// Real-Time Mode:批长只是 checkpoint 间隔
.trigger(Trigger.RealTime("10 seconds"))
.outputMode("update")   // RTM 要求 update 模式

一行 trigger 的差别,背后是两套调度模型。但这里有个今天必须说清楚的边界:RTM 目前只覆盖无状态算子——map-like 变换、union、流与静态表的 join。带聚合、带状态、带流流 join 的查询,还是走微批。老版本的 Continuous Processing 延迟能到 1 毫秒,但只有 at-least-once,官方已把它归为遗留方案。

所以「Spark 也能做到毫秒」这句话,得补一个定语:无状态链路可以,有状态还不行。而这恰恰是 Flink 的地盘。

三、分野二:状态归属

在这里插入图片描述

流处理真正的分水岭在这里。状态放哪儿,决定了三件事:能算多大、故障后多久恢复、能不能弹性扩缩。

方案 状态在哪 恢复要做什么 弹性
进程堆内 JVM 堆 全量快照回填 停-改-起
本地盘 + 远端快照 TM 本地盘 增量回填,与状态量成正比 停-改-起
本地盘 + changelog 本地盘,变更写 Kafka 重放 changelog 主题 再均衡迁移任务
远端对象存储 对象存储 拉元数据 + 按需拉文件 计算无状态,直接扩

Flink 的传统答案是第二行:状态在 TaskManager 本地,Checkpoint 定期快照到 HDFS 或 S3。这个模型很成熟,代价是恢复时间与状态量成正比——一个 2 TB 状态的作业冷启动,十分钟起步是常态,因为要把几个 T 搬回本地。

Kafka Streams 走的是第三行,思路很巧:本地 RocksDB 保证读取快,每次状态变更同时写一条到 changelog 主题,任务迁到别的实例就重放 changelog。它把「状态持久化」这件事外包给了 Kafka 自己的副本机制,所以一个 jar 包就能有容错。代价是绑死 Kafka,而且 join 前必须保证两边分区数一致、分区策略一致(co-partitioning),否则匹配不上。

RisingWave 这批走第四行:状态的 LSM 文件直接写对象存储,计算节点不存状态,checkpoint 做到 1 秒级,挂了就回滚到上一个 epoch 重放。计算节点无状态,扩缩容不需要 savepoint,直接加减副本。

Flink 2.x 的 ForSt 正在往第四行挪:SST 文件放对象存储,本地盘退化成缓存,Checkpoint 不再需要把全量状态推上去。方向是对的,但今天仍是 experimental——不支持 canonical savepoint,也不支持全量快照。这意味着一旦切过去,回退路径很窄。AWS 的托管 Flink 2.3 更是直接把它列进了「不支持」清单。

选型时该问的不是「状态能放多大」,而是「状态坏了,你多久能恢复」。这两个问题的答案,往往不在同一行。

四、分野三:交付形态

这条最容易被技术对比忽略,但它决定了你团队要养几个人。

Flink 是一个集群。哪怕是 Application 模式,你也要有 JobManager、TaskManager、Checkpoint 存储、指标监控、升级流程。它的好处是边界清晰:作业是作业,服务是服务,两边互不干扰,一个作业的 GC 不会拖垮另一个。

Kafka Streams 反过来,它是你服务的一部分。加副本就是加并行度,滚动发布就是升级,K8s 的探针、日志、链路追踪全都复用。代价是:你的业务服务里多了一块有状态的计算逻辑,一个分区卡住,整个 Pod 的健康检查都可能挂。

流式数据库则是第三种:你连一个 PostgreSQL,写 SQL,查物化视图。没有作业概念,没有 checkpoint 参数,也没有 DataStream API。它的边界是硬的——SQL 表达不了就是表达不了,没有 MATCH_RECOGNIZE,没有自定义算子,复杂状态机只能绕。

这三条没有优劣,只有「你的团队结构适配哪一种」。有平台组,选第一种;服务化彻底、Kafka 是中枢,选第二种;只有数据分析师,选第三种。

换成三个更具体的问题会更好判断:升级谁做、扩容谁做、凌晨三点出事谁看。 第一个问题筛掉没有平台组的团队,第三个问题筛掉没有 on-call 的团队。答不出来的,说明这套东西还没到能上生产的阶段,跟选哪个引擎无关。

五、同一个需求,四种写法

抽象讲完,落到一个具体需求上:统计每分钟各品类的 GMV 和去重订单数。

Flink SQL 是声明式的,窗口用 TVF:

SELECT window_start, window_end, category,
       COUNT(DISTINCT order_id) AS orders,
       SUM(amount) AS gmv
FROM TABLE(TUMBLE(TABLE orders, DESCRIPTOR(event_time), INTERVAL '1' MINUTE))
GROUP BY window_start, window_end, category;

Structured Streaming 写法几乎同构,只是多了个水位线:

orders
  .withWatermark("event_time", "30 seconds")
  .groupBy(window($"event_time", "1 minute"), $"category")
  .agg(countDistinct($"order_id").as("orders"), sum($"amount").as("gmv"))
  .writeStream.outputMode("update")
  .option("checkpointLocation", "s3://ck/gmv")
  .toTable("gmv_1m")

RisingWave 更短,因为它把「持续维护」做成了视图的默认语义:

CREATE MATERIALIZED VIEW gmv_1m AS
SELECT window_start, category,
       COUNT(DISTINCT order_id) AS orders, SUM(amount) AS gmv
FROM TUMBLE(orders, event_time, INTERVAL '1 minute')
GROUP BY window_start, category;

Kafka Streams 最长,因为 DSL 里没有 countDistinct:

orders.groupBy((k, v) -> v.getCategory())
      .windowedBy(TimeWindows.ofSizeAndGrace(
          Duration.ofMinutes(1), Duration.ofSeconds(30)))
      .aggregate(Gmv::new, (k, v, agg) -> agg.add(v), Materialized.as("gmv"))
      .toStream()
      .to("gmv_1m", Produced.with(timeWindowedSerdeFrom(String.class), jsonSerde));

注意最后一段:aggregate 只能累加,去重要么在 add 里维护一个订单 ID 集合(状态会涨),要么先按订单去重成一张 KTable 再聚合。这不是缺点,是库的边界——它假设你愿意自己拼装。

四段代码看下来,语法在趋同,真正的差别在后面那张表。

先看一个具体的坑:COUNT(DISTINCT)。Flink 在 upsert 流上要维护每个 key 的去重集合,状态随基数和窗口长度线性涨;Spark 的 countDistinct 在 update 模式下会往下游发 retract(撤回)消息,Sink 必须能处理「减一」;RisingWave 靠增量视图维护自己算 delta,用户看不见但代价在 join 的复杂度上;Kafka Streams 干脆不提供,让你自己拼。同一个函数名,四种状态代价。 这类细节不会出现在任何一张对比表的格子里,但它决定了你的作业三个月后会不会 OOM。

方案 正确性靠什么 出错之后你要做什么
Flink Checkpoint + Sink 两阶段提交 调间隔、调超时、盯反压
Spark SS Checkpoint + 幂等 Sink 保证 Sink 可重放或幂等
Kafka Streams Kafka 事务(EOS v2) 确认 transactional.id 与超时
RisingWave 秒级 barrier 快照 基本不管,等 epoch 回滚

这就是选型里最容易被跳过的一列。四种方案都能做到精确一次,但「做到」的成本不一样:Flink 把成本放在运维(你要懂 checkpoint 和事务超时的相互约束),Kafka Streams 放在事务配置(transaction.timeout.ms 与 broker 上限要对齐),RisingWave 放在表达力(SQL 写不出的就只能放弃)。

如果要做 POC,别比 TPS。同一份数据、同一个查询,量三个数:端到端 p99 延迟、状态占用、以及从 kill -9 到重新出数的秒数。第三个最容易被忽略,也最能拉开差距——它直接决定了故障时你敢不敢重启。

六、五个还在流传的误区

「Flink 一定比 Spark 快」。 快慢取决于负载形状。全链路无状态、要求毫秒,RTM 已经够用;一旦有窗口聚合和大状态,微批的调度开销反而被摊薄,差距缩小。拿一条 map-filter 链路去比,结论没有意义。

「微批就是秒级」。 地板约 100 毫秒,不是秒。真正的秒级来自「批长设成 10 秒」这种配置选择,不是模型限制。

「Kafka Streams 只能 at-least-once」。 EOS v2 早就有了,开 processing.guarantee=exactly_once_v2 即可。它的精确一次比 Flink 简单,因为事务边界就是 Kafka 自己的。

「流式数据库会取代 Flink」。 在 SQL 能表达的范围内,它确实更省事。但 CEP、自定义算子、复杂状态机、非 Kafka 的源,它接不住。RisingWave 官方博客给过一组 Nexmark 数据,27 个查询里赢 22 个——这是它自测口径,值得看,但要按自己的查询形状复测。

「开源能跑,托管就能用」。 这条最坑。AWS 托管 Flink 2.3 明确不支持物化表、ForSt、原生 S3、Java 21。你在本地验证过的 2.3 新特性,上云可能一个都用不了。选型时要问的不是「Flink 2.3 有什么」,而是「你打算用的那个托管版本,有哪些」。

七、四层筛:剩下的才是 Flink

在这里插入图片描述

把前面的东西压成四道筛子,从上往下过:

  1. 延迟容忍超过 1 分钟? 日报、对账、特征回填,留在 Spark 微批或者直接调度批处理。你已经有湖仓,别为了「实时」再建一套。
  2. 只读写 Kafka,且按服务切分? 一个依赖就能上线,Kafka Streams 更划算,还没有集群要养。
  3. 只用 SQL,还要自带查询服务? RisingWave / Materialize,省掉一个 serving 层。
  4. 要 CEP、自定义算子或 TB 级状态? 到这一步,才轮到 Flink。此时要算的不是「它能不能做」,而是「你有没有人盯 checkpoint、水位线和反压」。

四层都过,Flink 才是那个划算的选项。它不是「更快」,而是少数能把毫秒延迟、大状态、精确一次同时兜住的引擎——这个组合,今天仍然没有第二个开源方案能完整覆盖。

还要承认一个现实:这套筛子选出来的往往不是一个引擎,是一张分工图。 多数中型以上公司最后是 Spark 管批、Flink 管流,或者 Flink 管核心链路、Kafka Streams 管服务内的轻量聚合。这不是妥协,是成本最优——把一个每天跑一次的离线任务硬搬到流引擎上,你付的是 24 小时常驻资源的钱。

八、最后

Flink 2.3 这半年做的事,其实是在把边界往外推:物化表支持 START_MODE,改一条定义不用重放两年历史;FROM_CHANGELOG / TO_CHANGELOG 让 SQL 层能自由转换追加流和变更流;原生 S3 甩掉 Hadoop 依赖,插件包从 30–93 MB 降到 13 MB 左右。这些都在吃掉流式数据库和轻量方案的地盘。

但推边界不等于消灭对手。只要「SQL 表达不了」和「团队没人运维」这两个前提还在,别的路线就永远有位置。

所以对比的终点从来不是排名,是你愿意维护哪一层抽象。三条分野问完,答案往往不是「谁更强」,而是「哪种代价我付得起」。

你们现在这套实时链路,最难维护的是哪一块——调度、状态,还是人?

相关实践学习
基于Hologres+Flink搭建GitHub实时数据大屏
通过使用Flink、Hologres构建实时数仓,并通过Hologres对接BI分析工具(以DataV为例),实现海量数据实时分析.
实时计算 Flink 实战课程
如何使用实时计算 Flink 搞定数据处理难题?实时计算 Flink 极客训练营产品、技术专家齐上阵,从开源 Flink功能介绍到实时计算 Flink 优势详解,现场实操,5天即可上手! 欢迎开通实时计算 Flink 版: https://cn.aliyun.com/product/bigdata/sc Flink Forward Asia 介绍: Flink Forward 是由 Apache 官方授权,Apache Flink Community China 支持的会议,通过参会不仅可以了解到 Flink 社区的最新动态和发展计划,还可以了解到国内外一线大厂围绕 Flink 生态的生产实践经验,是 Flink 开发者和使用者不可错过的盛会。 去年经过品牌升级后的 Flink Forward Asia 吸引了超过2000人线下参与,一举成为国内最大的 Apache 顶级项目会议。结合2020年的特殊情况,Flink Forward Asia 2020 将在12月26日以线上峰会的形式与大家见面。
目录
相关文章
|
云安全 安全 网络安全
|
3天前
|
人工智能 中间件 Java
LangChain 到底是什么:从链式连接到 Agent 运行时,讲透 v1.0 的核心概念
LangChain 是大模型应用开发框架,非单纯API封装。v1.0聚焦Agent工程化:以`create_agent`为核心,基于LangGraph实现可持久化、可中断、可观测的智能体循环;统一模型接口、强化工具描述、分层记忆管理,并通过中间件支持人工介入、摘要压缩与隐私脱敏,推动AI从“能跑”走向“可靠生产”。(239字)
135 1
|
6天前
|
人工智能 JSON 自然语言处理
LangChain 六大使用场景:哪些值得做,哪些不值得,一张决策图说清
本文系统梳理LangChain六大核心场景(RAG、Agent、对话系统、结构化提取、内容生成、多模态),逐项解析痛点、机制、落地要点与适用边界,并附“不该用”的反向清单。强调“模型+外部世界”协同才是框架价值所在,助力AI工程化理性选型。
241 0
|
19天前
|
人工智能 弹性计算 数据库
阿里云最新优惠券种类及领取和使用指南:学生无门槛券、AI体验券减35元与按量达标返100元
阿里云优惠券是一套按适用主体、产品范围、触发条件与有效期交叉划分的权益矩阵,2026年主要涵盖300元无门槛券、AI体验券、满减优惠券、AI新品券及算力券等类型。学生经学信网认证可领300元无门槛券(有效期12个月),适用于全量公共云产品;AI体验券合计60元、三档满减最高立减35元,需特邀资格且用券期仅14天;满减券限大促新购或升级,最高满1万减800元;"AI用量加速计划"则按月度实付达标次月返还,最高单月100元。使用需重点核验资格、门槛、订单类型与叠加互斥规则。
|
7天前
|
人工智能 编解码 API
漫剧动态 AI 视频批量生成完整方案(Python+ComfyUI + 万相 Animate,适配短剧漫剧流水线)
本文提供一套可部署的AI漫剧自动化生产方案:基于Python调度+ComfyUI+万相Animate模型,实现“分镜图→动态镜头→成片”全流程。支持批量处理、自动拼接与字幕添加,专治闪脸、卡顿、导出低效等痛点,12G显存即可运行。(239字)
|
1月前
|
存储 JSON API
百炼声景模型新成员实测:Qwen-Audio-3.1-TTS-Next 的调用路径、时序控制与成本边界
阿里百炼新推Qwen-Audio-3.1-TTS-Next(9月21日上线),支持文本一键生成含人声、音效、环境声的时序化声景,告别单音色朗读。实测覆盖接口调用、时序控制与成本核算,提示词≤3000字符,成品≤120秒,输出200 token/秒,约0.24分/秒。
百炼声景模型新成员实测:Qwen-Audio-3.1-TTS-Next 的调用路径、时序控制与成本边界
|
1月前
|
算法 数据挖掘 Shell
经验自进化:自动挖掘经验资产,消融实验验证真实收益丨AgentLoop 数据飞轮实践(五)
本文介绍AgentLoop经验自进化实践:从运行轨迹自动挖掘成功与失败模式,经Skill召回并注入上下文。文章详解接入验证流程,并通过消融实验优化召回策略,降低耗时、成本、Token消耗和工具调用,让Agent持续迭代。
410 15
|
10月前
|
缓存 监控 测试技术
llama.cpp Server 引入路由模式:多模型热切换与进程隔离机制详解
llama.cpp 于2025年12月11日发布路由模式,支持多模型动态加载与毫秒级切换,无需重启服务。采用多进程隔离架构,兼容OpenAI API,支持自动发现、按需加载、LRU淘汰及手动管理,显著提升本地多模型协作的效率与稳定性,是轻量级推理服务框架的重要升级。
2865 3
llama.cpp Server 引入路由模式:多模型热切换与进程隔离机制详解
|
安全 Linux
使用squid在阿里云服务器(centos7)上搭建自己的代理服务器
大部分公司的内网往往有各种网络限制,无法访问部分网站。所以弄个服务器来代理一下,翻个公司的内网还是很舒服的。在此,我记录的是用squid来完成的代理服务器。   安装 检测是否安装: # which squid /usr/bin/which: no squid in (/usr/local/sbin:/usr/local... 出现以上提示说明没有安装。
9256 0

热门文章

最新文章