尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
流计算选型实战:Flink、Storm、Spark Streaming核心对比与案例解析
聊流计算之前我先说一个我真实经历过的场景。某次业务方提了一个需求做一个实时大屏要求数据从产生到出现在大屏上不超过5秒钟。我下意识打开之前的离线调度任务准备沿用每天凌晨跑一次、第二天看结果的方式结果被业务方一句话顶了回来我要的是现在不是昨天。就是从那次开始我才真正意识到流计算解决的核心矛盾不是算得快而是数据从产生到被消费之间的时间差。批处理把这些差值压缩到T1流计算则追求数据到达即处理结果以秒级甚至毫秒级延迟对外可见。在流计算这个领域里绕不开四个名字Flink、Storm、Spark Streaming还有一个Spark。很多刚接触的人会把它们当成并列关系实际上Spark本身是批处理引擎Spark Streaming只是它上面的流处理扩展而Flink和Storm从一开始就是为实时数据设计的。这篇文章不打算做教科书式的原理宣讲我想从实际选型和使用者的视角把这四个名字背后的处理模型、实战路径和真实代价讲清楚尽量让准备入手流计算的人少走弯路。1. 从明天看报表到现在看数据流计算解决的核心矛盾流式计算的价值本质上取决于业务对数据新鲜度的要求。今天很多场景已经不是数据有没有的问题而是数据晚到几秒钟业务就受不了的问题。1.1 批处理为什么在很多场景下不够用批处理的核心思路是攒够一批再算。离线任务把一天的数据通过调度系统在某个时间点集中拉取经过清洗、关联、聚合最终产出报表。它的问题不是不能算而是算得太晚。举个例子一个交易系统想识别同一张银行卡在短时间内高频消费的异常行为如果等T1的批处理跑完用户在第二天才收到风控提示这个提示已经失去了拦截意义。另外一个容易忽略的点是批处理虽然延迟高但它对资源的使用往往是峰值集中的。每天凌晨跑批时计算资源被打满白天则大量空闲。这种资源模型遇到实时性要求高的业务时完全失效因为数据是持续产生的处理系统也需要持续运行而不是集中在某个时间节点集中爆发计算。1.2 流计算最典型的几类应用场景流计算最常见的落地场景我总结下来有三类。第一类是实时监控和可视化比如大屏上的交易量、在线用户数、设备状态这类场景对延迟极其敏感5秒以上就会觉得不实时。第二类是实时风控和反欺诈系统需要根据用户行为序列判断风险数据每多延迟一秒风险扩散的范围就大一分。第三类是实时ETL和指标计算把业务数据库的变更通过变更数据捕获同步到数据仓库或下游系统这时候流的角色是数据管道而不是分析引擎。还有一个场景是流式推荐。用户点击、浏览、加购的行为不断产生推荐系统需要根据最近几分钟的行为实时调整推送内容。这就是为什么很多互联网公司对流计算的需求非常旺盛——他们的业务本身就是现在发生什么现在就要响应什么。1.3 实时这个词在不同框架里的含义不同这里要先埋一个伏笔不同流处理框架对实时的定义完全不一样。Storm是纯事件驱动的每条数据到达后立刻被处理延迟在毫秒级Spark Streaming把流切成秒级的小批次所以它的延迟下限就是一个批次的大小Flink则介于两者之间它是真正的流式处理但也支持按时间窗口做聚合延迟取决于窗口策略。后面我会详细展开这三个模型的区别现在只需要建立一个大概念实时不是一种状态而是一个范围选型之前先要搞清楚业务到底能容忍多少秒的延迟。2. 三大引擎的本质差异流式、微批次、纯事件驱动分别意味着什么把Flink、Storm、Spark Streaming放在一张表里对比是理解它们差异最快的方式。我先把结论摆出来然后逐个拆解。维度StormSpark StreamingFlink处理模型原生流式逐条处理微批次Micro-batch原生流式批流一体延迟毫秒级秒级取决于批次间隔毫秒到秒级取决于窗口状态管理较弱多依赖外部存储支持基于StateStore内置状态后端支持大状态时间语义处理时间为主处理时间为主后支持事件时间事件时间加水位线原生支持精确一次语义需结合外部系统实现Spark 2.x起支持内置checkpoint原生支持API抽象Spout/Bolt偏底层DStream / Structured StreamingDataStream / Table SQL2.1 Storm真正的一条一条处理Storm是我最早接触的流式框架。它的核心抽象是拓扑一个拓扑由Spout和Bolt组成Spout从数据源读取消息Bolt负责处理每个Spout或Bolt在集群上并行运行多个实例。Storm的原始模型就是数据一条一条流过不攒批、不等待天然适合毫秒级延迟的场景。但这种设计的代价是为了提高吞吐Storm对消息的可靠性保障做得比较吃力。它通过一种ack机制来追踪每条消息是否被完整处理如果超时未确认就重新发送。这个机制在低流量下没问题一旦流量暴涨大量消息元的追踪本身就会成为性能瓶颈。Storm后来推出的Trident抽象就是为了解决可靠性问题但它实际上又回到了微批次模型等于把优势让了出去。2.2 Spark Streaming把流切成批来处理Spark Streaming的思路完全是另一种角度。它不做逐条处理而是把连续不断的流按照设定的间隔比如1秒或5秒切成一段一段的小批次每一批对应Spark的一个作业再交给Spark Core去算。这个过程对用户是透明的你写的是DStream操作底层跑的却是批处理作业。微批次模型的优点非常明显它继承了Spark Core的容错机制、调度机制和丰富的算子开发门槛比Storm低很多。而且由于批次是定长的系统的吞吐可以做得比较高资源利用也更稳定。缺点同样明显延迟被批次间隔锁死就算你把批次设成1秒也意味着必须攒够1秒的数据才能触发一次计算做不到逐条响应的毫秒级延迟。2.3 Flink流式计算优先批处理是流式的一种特例Flink的设计哲学跟Storm和Spark Streaming都不一样。Flink认为流式是数据的天然形态批处理只是流的一个边界情况——把所有数据当作一个有限流来处理。所以在Flink里核心的数据模型是无限流加上窗口Window窗口把无界数据切分成有界范围来做聚合和关联。Flink对事件时间Event Time的支持是它相比前两者最大的技术护城河。真实世界中数据产生的时间和处理的时间经常不一致消息可能在网络里乱序延迟。Flink用事件时间和水位线Watermark机制处理乱序问题在窗口计算中等待迟到的数据同时保证实时性。这套机制在金融、风控等对时序敏感的领域几乎是刚需而Storm和早期的Spark Streaming都做不到这么精细。3. Flink实战本地环境、核心API与checkpoint配置我不打算空谈原理直接分享一段我曾经跑通的Flink流式作业。如果你身边有一台电脑哪怕配置一般也可以跟着走一遍。3.1 本地环境准备不需要先搭集群很多人一听到Flink就以为必须部署一套集群才能跑这是一个很大的误区。Flink支持本地模式最简单的做法是下载发行包然后启动一个本地集群或者干脆在IDE里用Maven/Gradle建一个工程依赖里引入Flink的Java客户端库即可。我推荐第二种方式因为方便调试代码跟生产环境是同一套API。需要的依赖大致是flink-streaming-java和flink-clients版本建议统一。我在本地用Gradle建工程时grouptoplevel只引入两个模块一个是org.apache.flink:flink-streaming-java另一个是org.apache.flink:flink-clients运行环境是JDK8就能把最简单的流作业跑起来。这里有一个容易踩的坑Flink对Scala和Java的版本有对应要求如果你用Scala API一定要注意Scala二进制版本是否匹配否则运行时会报方法不存在之类的异常。3.2 第一个实时WordCount用Socket当数据源下面这个例子是流式WordCount数据源是Socket端口。打开一个终端执行nc -lk 9999随便输入一些单词Flink作业就会实时累计每个单词出现的次数。先看代码import org.apache.flink.api.common.typeinfo.Types; import org.apache.flink.api.java.tuple.Tuple2; import org.apache.flink.streaming.api.datastream.DataStream; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; import org.apache.flink.util.Collector; public class StreamWordCount { public static void main(String[] args) throws Exception { StreamExecutionEnvironment env StreamExecutionEnvironment .getExecutionEnvironment(); DataStreamString text env.socketTextStream(localhost, 9999); DataStreamTuple2String, Integer counts text .flatMap((String line, CollectorTuple2String, Integer out) - { for (String word : line.split(\\s)) { out.collect(new Tuple2(word, 1)); } }) .returns(Types.TUPLE(Types.STRING, Types.INT)) .keyBy(tuple - tuple.f0) .sum(1); counts.print(); env.execute(Stream WordCount); } }这段代码里有几个关键信息。socketTextStream表示数据源是不间断的流keyBy把相同单词分到同一个分区sum(1)对Tuple2的第二个字段做累加。注意.returns(...)不是可有可无的Java的Lambda表达式在Flink里经常因为类型擦除丢信息如果不显式指定返回类型作业提交时可能直接报类型错误。这里有一个新手容易忽略的点print()在本地模式下直接输出到控制台但在集群上它会输出到TaskManager的日志里。很多人在集群上跑完发现控制台没输出东西以为任务没执行其实数据早就写到日志中了。调试时用print没问题生产环境建议直接接到Kafka或者别的下游系统。3.3 checkpoint与状态后端上生产前必须做的事本地跑通WordCount只是第一步。真正把Flink作业部署到生产环境前有两件事必须做配置checkpoint和选好状态后端。checkpoint是Flink容错的基石它周期性地把任务的状态和位置保存到持久存储中一旦任务失败就从最近一次checkpoint恢复。没有checkpoint的Flink作业一旦节点宕机状态会丢得一干二净这对于有状态计算是致命的。配置方式很简单在创建环境之后加几行env.enableCheckpointing(10000); // 每10秒做一次checkpoint env.getCheckpointConfig().setMinPauseBetweenCheckpoints(5000); env.getCheckpointConfig().setCheckpointTimeout(60000); env.getCheckpointConfig().setMaxConcurrentCheckpoints(1); env.setStateBackend(new EmbeddedRocksDBStateBackend());这里我解释一下几个参数的意义。enableCheckpointing(10000)是checkpoint周期周期越短恢复时丢失的数据越少但checkpoint本身也会占用资源setMinPauseBetweenCheckpoints是两次checkpoint之间至少间隔多久防止前一个还没完成就启动下一个setCheckpointTimeout是单次checkpoint的超市时间防止状态太大导致deadline无限拉长。状态后端我推荐RocksDB它能支持超大状态把状态落到本地磁盘而不是全部挤在内存里。还有一个跟事件时间相关的坑。Flink要处理乱序事件需要设置水位线策略。常见做法是WatermarkStrategyString strategy WatermarkStrategy .StringforBoundedOutOfOrderness(Duration.ofSeconds(5)) .withTimestampAssigner((event, timestamp) - extractTimestamp(event));forBoundedOutOfOrderness(5秒)的意思是我允许最多5秒的乱序超过5秒才算迟到。这个值设得越大窗口触发越晚实时性越差设得越小迟到的数据越多计算结果越不准。具体设多少要根据数据源的乱序程度来测不能拍脑袋。4. Spark Streaming的两条路线DStream和Structured Streaming怎么选Spark生态里跟流计算相关的有两套API老牌的DStream和后来的Structured Streaming。很多人一上来就被这两个名字搞晕我先说结论新项目尽量用Structured StreamingDStream已经处于维护状态只在老项目里会遇到。4.1 DStream API经典RDD序列DStream的全称是Discretized Stream离散化流。它的核心思想是把连续流按时间间隔切分每一个间隔内的数据形成一个RDD然后对RDD做各种转换操作。你可以把DStream理解为RDD的序列每一批数据本质上是Spark Core在处理。写DStream代码的体验跟写批处理很接近。比如同样是WordCountDStream版本大致是这样val stream ssc.receiverStream(new SocketReceiver(localhost, 9999)) stream .flatMap(_.split( )) .map(word (word, 1)) .reduceByKey(_ _)问题在于DStream的窗口操作、状态操作全部基于处理时间Processing Time它感知不到事件产生的时间。如果你的业务数据存在延迟到达的情况DStream处理出来的结果会漂移得很厉害。我在一个日志分析项目里就遇到过日志落盘时间跟Kafka收到时间差了十几分钟按处理时间做窗口结果完全对不上业务口径。4.2 Structured Streaming从流到表的抽象Structured Streaming把流式数据当成一张不断追加的表来处理。每一秒到达的新数据就是往表里插入新行而用户的查询语句写在这张表上引擎会持续输出结果。这个抽象最大的好处是你可以用处理DataFrame的思维来处理流API是统一的。同样的WordCount用Structured Streaming的Python API写出来是这样from pyspark.sql import SparkSession spark SparkSession.builder.appName(word_count).getOrCreate() lines spark.readStream.format(socket) \ .option(host, localhost) \ .option(port, 9999) \ .load() words lines.selectExpr(explode(split(value, )) as word) counts words.groupBy(word).count() query counts.writeStream \ .outputMode(complete) \ .format(console) \ .start() query.awaitTermination()注意readStream和writeStream这两个方法它们对应批处理里的read和write。Structured Streaming支持事件时间窗口、水位线还支持用append、update、complete三种输出模式控制结果表的写法。相比DStream它在语义上完整得多。4.3 Spark Streaming的适用边界在哪里虽然Structured Streaming是当前推荐路线但我不认为老的Spark Streaming就一无是处。如果你已有的Spark批处理任务非常多团队对DataFrame API相当熟想用同一套栈覆盖批和流那么Spark生态天然是首选。它的优势在于构建在Spark Core之上历史包袱少、社区用户多、排查问题资料丰富。代价就是延迟下不来特别在意毫秒级响应的场景不适合它。我个人的经验是如果延迟可以接受2秒以上且整体技术栈已经重度绑定了Spark那就没必要硬上Flink如果业务对延迟和精确性要求都很高那就不要纠结直接选Flink。5. Storm的价值重估低延迟之外还有哪些不可替代性很多人现在聊流计算基本都聚焦在Flink和Spark Streaming上Storm好像成了上古技术。我觉得这种判断有点一刀切。Storm确实在主流实时计算场景中逐渐退场但它代表的纯事件驱动理念和它身上的一些特性在某些场景下仍然有用。5.1 Spout和Bolt的拓扑结构Storm的程序模型是拓扑。拓扑里最核心的两个组件是Spout和BoltSpout是数据源负责从Kafka、消息队列或日志文件中读取消息Bolt是处理节点可以执行过滤、聚合、关联等操作。多个Spout和Bolt通过数据流连接成一张有向图就是整个拓扑。这种模型非常直观。我曾在某个物联网项目里用Storm处理设备上报的传感数据每条数据经过解析、清洗、阈值判断三个Bolt就能触发告警。因为不攒批数据到达后确实能做到毫秒级处理这在当时的物理网设备告警场景下是刚需。5.2 ack机制如何保证可靠性Storm为了保证消息不丢设计了一套acker机制。每个Spout发出的消息带上一个消息IDacker负责追踪这条消息经过的所有Bolt是否执行成功。如果某条消息在超时时间内得不到应答Spout会重新发送。这里有一个典型权衡开启ack机制会影响吞吐因为每一条消息都需要在acker层面记录状态。如果关闭ack吞吐大幅提升但消息丢失概率也上升。我在一个流量超大的场景中测试过开启ack后吞吐下降将近一半。所以Storm的使用者必须做好取舍不是每条消息都需要可靠保障的。5.3 哪些场景还在用Storm目前还在坚持用Storm的场景我观察下来主要是两类。第一类是已经把大量拓扑跑稳定了、迁移成本极高的存量系统它们没有足够的动力重写。第二类是对延迟极度敏感且消息量不是特别大的场景比如证券行情推送、实时告警。这类场景里Storm的毫秒级处理能力和简洁模型反而比Flink的厚重更合适。不过客观地说Flink在延迟和可靠性两方面都已经超越了Storm且运维生态更完善。如果你是一个新项目我不太建议再从Storm起步。了解Storm的价值更多是为了读老代码或者在技术方案答辩时能说清楚为什么不用Storm。6. 选型不是背参数结合业务场景的最终权衡与踩坑记录技术选型的本质是把业务需求翻译成引擎能力。不同引擎的参数差异网上到处都有但怎么把这些差异映射到具体业务才是真正考验人的地方。6.1 延迟优先场景风控和交易系统我做过一个交易风控项目要求从交易发生到风险判定必须在1秒内完成。这个需求直接排除了Spark Streaming因为就算把批次间隔压到500毫秒加上计算和调度开销也很难稳定控制在1秒内。当时我在Flink和Storm之间犹豫。最终选Flink的原因有两个一是我们需要根据用户的跨渠道行为做关联分析这需要强大的状态管理能力Flink内置状态后端显然比Storm的乱糟糟实现更可靠二是Flink的exactly-once语义能避免重复计算Storm在极端情况下可能重复触发风控规则这对用户是极大的困扰。6.2 吞吐优先场景日志ETL和数据管道如果业务只是把埋点日志从Kafka实时搬运到数据仓库中间做一些解析、过滤、字段映射对延迟要求是别超过一两分钟那Flink和Spark Streaming都能干。这种场景我倾向于选Spark生态因为数据仓库本身就是Spark的天下复用同一套资源池和调度体系可以降低运维复杂度。尤其是如果下游还连着Spark SQL做即席查询统一为Spark技术栈会省很多沟通成本。6.3 团队技术栈与运维成本选型必须考虑团队里的人是写Java多还是Scala多、之前有没有维护过实时集群的经验。Flink的Java API非常成熟运维上需要注意的状态后端、checkpoint、反压监控等概念比较多Spark Streaming则依赖Spark集群运维体系跟离线任务是一致的。如果团队里只有一个懂实时计算的人我建议优先选择这个团队最熟的技术栈而不是根据基准测试报告里那几毫秒的差距来决策。实时系统的故障排查远比性能峰值更消耗人力。6.4 我在几个项目里踩过的实际坑最后分享几个真实踩过的坑。第一个坑是Kafka消费者的并行度设置。Flink里Kafka Source的并行度受限于Kafka Topic的分区数如果你把Source并行度设得比分区数还大多出来的并行度是空转。反过来如果想提升吞吐先加Kafka分区再调Flink并行度。第二个坑是窗口计算的输出延迟。很多人设了5分钟的滚动窗口以为第5分钟一到就能出结果。实际上如果数据源存在乱序窗口要等水位线经过才真正触发。我在测试环境里用乱序数据验证过最终输出比预期的窗口结束时间晚了将近半分钟这是正常现象不是Bug。第三个坑是状态后端的选择。RocksDB能存大状态但访问速度比堆内存慢如果状态小且内存充裕用HashMap状态后端的性能更好。我见过有项目把所有状态都塞进RocksDB结果吞吐上不去换回堆内存瞬间就好了。实时计算是个跟业务强相关的领域每个场景的坑都不太一样。这恰恰是它的有意思之处比的不只是能不能算还比在真实数据面前能不能稳。上面这些经验和代码都是我一次次跑到生产环境后排出来的。如果你正在几个引擎之间摇摆最好的办法不是反复看对比文章而是把真实业务的一小段流量接到候选引擎上跑几天用数据说话。
RELATED

相关推荐

open-code-review:面向工程落地的代码审查编排器

open-code-review:面向工程落地的代码审查编排器

1. 这不是又一个“AI代码审查”玩具:open-code-review 的真实定位与生存逻辑 你搜过“open-code-review”这个词吗?我搜过——不是在 GitHub 上点开仓库看 star 数,而是翻了整整三天的 GitHub Issues、Discord 频道记录、PR 评论区&#xff0…

📅 2026/10/19 15:19:30
生产级RAG评测实战:可归因、可操作的端到端评测体系

生产级RAG评测实战:可归因、可操作的端到端评测体系

1. 这不是理论课,是能直接上手跑通生产环境的RAG评测实战手册“RAG评测体系保姆级教程:生产级实践指南”——光看标题,你可能以为这是又一篇堆砌指标、罗列公式、最后贴个Accuracy数字就收工的学术综述。但我要说,这恰恰是过去三年…

📅 2026/10/19 15:19:30
单周期CPU设计:数据通路与Verilog实现避坑指南

单周期CPU设计:数据通路与Verilog实现避坑指南

简介:这份资源是《计算机组成原理》实验课的单周期CPU设计完整作业包,面向正在学习MIPS32指令系统与处理器实现的高校学生及自学者。内容围绕Verilog HDL编写的单周期CPU展开,覆盖十六条指令的实现过程,帮助读者理解单周期CPU的工…

📅 2026/10/19 15:19:30
MORE NEWS

更多资讯

📰

MySQL读写分离与分库分表:Atlas中间件部署实战指南

我折腾MySQL中间件这几年,有一个很深的体会:大部分团队一开始根本不需要引入复杂的分布式数据库,一台主库配上几台只读从库,再架一层轻量级的访问代理,就能扛住绝大多数读写压力。这个代理层,在国内最经典、…

📰

考研计算机网络物理层:噪声对抗与信道生存实战指南

1. 这不是课本复述,而是考研物理层的实战拆解“计算机网络(考研)第二章 物理层”——看到这个标题,很多备考同学第一反应是:背概念、记公式、画分贝图、算奈氏准则和香农定理……然后发现做题时还是卡在“信道复用方式…

📰

MicroPython纯Python中文点阵字库:零依赖TFT显示方案

简介:本资源是一套专为MicroPython嵌入式开发设计的原创点阵字库及配套调用接口,面向STM32、ESP32等MCU平台的TFT显示屏开发者,解决中文显示缺乏轻量级、可移植字库方案的痛点。资源包含4种像素规格(1212/1616/2424/3232&#xff…

📰

葡萄酒评分统计建模:Wilcoxon检验与主成分分析实战

简介:这份资源是2012年全国大学生数学建模竞赛A题一等奖论文《葡萄酒的评价》的完整PDF文档,面向备战数学建模竞赛的高校学生、指导教师以及需要学习统计建模方法的读者,可用于赛题复盘、论文写作参考与方法迁移。压缩包内共1个PDF文件&#…

📰

开源可验证的代码评审范式:规则透明、执行可审计

1. 项目概述:这不是一个“工具”,而是一套可落地的开源代码评审范式“open-code-review”这个词乍看像某个 GitHub 仓库名,但实际它代表的是一种正在快速成型的新型工程实践——把传统上依赖人工经验、流程松散、结果难追溯的代码评审&#x…

📰

CTF Web入门题实战认知地图:从PHP弱类型到WAF绕过

1. 这不是“刷题清单”,而是一份Web安全初学者的实战认知地图你点开这个标题,大概率正卡在ctfshow-web入门题目的某个关卡里:可能是第32题那个看似简单的PHP弱类型绕过,反复提交却始终返回“no”;也可能是第37题的SQL注…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬