1. 流批一体概述:流式计算与批处理的概念、Lambda架构的痛点、Kappa架构的演进、流批一体的核心价值

1.1 流式计算与批处理:两种不同的“世界观”

先聊聊批处理。说白了,批处理就是“攒够了再干”。你把过去一天、一小时的数据攒成一个文件,然后一次性跑个 MapReduce 或者 Spark 任务,算出结果。我刚开始做大数据那会儿,用的就是这种模式。每天凌晨跑个定时任务,算昨天的报表。数据量大?没关系,慢慢跑,反正不赶时间。

流式计算呢?正好反过来。它是“来一条算一条”。数据像水流一样源源不断地进来,系统得在毫秒级内做出反应。比如你在淘宝上买东西,下单后立刻看到“库存-1”,这就是流计算在背后撑着。

这两种模式,核心区别在哪?我列个表,你一看就明白:

维度 批处理 流式计算
数据范围 全量数据(过去N小时/天) 实时到达的单条或微批次数据
延迟要求 分钟级~小时级 毫秒级~秒级
计算模型 一次性触发,全量扫描 持续运行,增量处理
典型引擎 MapReduce、Hive、Spark SQL Flink、Storm、Kafka Streams
状态管理 无状态(每次从头算) 有状态(维护中间结果)

你想想看,批处理就像拍照片,咔嚓一下,记录的是某个时间点的快照。流式计算呢,像录像,每一帧都在变化。这两种模式各有各的适用场景,但问题来了——很多业务场景,你既需要低延迟的实时结果,又需要全量数据的精确回溯。这就引出了经典的 Lambda 架构。

1.2 Lambda 架构的痛点:两套代码,双倍痛苦

Lambda 架构,我估计很多老哥都听过。它的思路很简单:

  • 批处理层:处理全量数据,保证最终结果的准确性。
  • 流处理层:处理实时数据,提供低延迟的近似结果。
  • 服务层:把两边的结果合并,对外提供服务。

听起来挺合理对吧?但我在项目中实际落地过之后,发现这玩意儿就是个“美丽的陷阱”。

⚠️ 我曾经踩过的坑: 有一次做实时大屏项目,批处理用 Hive,流处理用 Flink。两套代码逻辑完全一样,但实现方式天差地别。批处理写的是 SQL,流处理写的是 DataStream API。结果呢?业务逻辑改一次,两边都得改,还经常改出不一致。上线后数据对不上,排查了三天,最后发现是批处理和流处理对“去重”的理解不一样。

Lambda 架构的痛点,我总结为三点:

  1. 维护两套代码:批处理和流处理各写一套逻辑,开发成本翻倍。你想想看,同样的业务逻辑,用 SQL 写一遍,再用 Flink API 写一遍,还得保证结果一致。这不是折磨人吗?
  2. 数据一致性难保证:批处理的结果和流处理的结果,在服务层合并时,经常出现“对不上”的情况。比如实时层算出的 UV 是 100 万,批处理层算出来是 98 万。到底信哪个?
  3. 运维复杂度高:两套集群、两套调度、两套监控。出问题的时候,你得同时排查两个系统。我记得有一次半夜被叫起来,说实时大屏数据不动了。查了半天,发现是批处理层的 Hive 任务把临时表给覆盖了,导致服务层的数据被污染。

说白了,Lambda 架构就是“用复杂度换实时性”。在数据量不大、业务逻辑简单的时候还能凑合。一旦规模上来,这玩意儿就是个定时炸弹。

1.3 Kappa 架构的演进:用一套引擎统一天下

既然 Lambda 架构这么痛苦,那有没有更好的方案?有,就是 Kappa 架构。

Kappa 架构的核心思想很简单:只用一套流处理引擎,搞定所有事情。批处理?不存在的。你把全量数据也当成流,从头到尾“重放”一遍,不就等于做了一次批处理吗?

我举个例子。假设你要算过去 30 天的用户活跃数。在 Lambda 架构里,你得用批处理跑全量数据,再用流处理跑实时增量。但在 Kappa 架构里,你只需要把过去 30 天的数据全部重新输入到流处理引擎里,让它从头算一遍。结果一样,但只用了一套代码。

💡 核心思想: Kappa 架构认为,批处理只是流处理的一个特例——只要数据源支持重放(比如 Kafka 可以回溯消费),流处理引擎就能通过“重新计算”来达到批处理的效果。

Kappa 架构的演进,其实反映了业界的一个共识:流和批的本质是一样的,只是时间窗口不同。批处理是“无限大的窗口”,流处理是“小窗口”。既然底层逻辑一样,为什么非要用两套引擎呢?

当然,Kappa 架构也不是银弹。它有个前提:你的数据源必须支持全量重放。比如 Kafka,你得把数据保留足够长的时间,或者有办法从外部存储(如 HDFS)重新导入。另外,如果数据量特别大,全量重放的成本也很高。但相比 Lambda 架构的两套代码维护成本,这点代价是值得的。

1.4 流批一体的核心价值:一套代码,两种语义

聊完了 Lambda 和 Kappa,咱们终于可以进入正题了——流批一体。

流批一体,说白了就是:用同一套 API 写代码,既能跑批处理,也能跑流处理。你写一个 SQL 或者 DataStream 程序,在批模式下跑,它就是批处理;在流模式下跑,它就是流处理。底层引擎自动优化执行计划,不需要你操心。

我个人认为,流批一体的核心价值体现在三个方面:

价值维度 具体体现 我的实际感受
开发效率 一套代码,两种运行模式 以前写两套代码要 2 周,现在 1 周搞定
数据一致性 批和流共享同一套状态和语义 再也不用半夜起来对数据了
运维简化 一套集群,一套监控 集群数量直接减半,运维成本下降 60%

你可能会问:流批一体到底是怎么做到的?核心在于 状态复用。批处理和流处理,本质上都是对数据的“有状态计算”。批处理的状态是“全量快照”,流处理的状态是“增量快照”。如果能把这套状态管理统一起来,那批和流就能共用一套计算逻辑。

举个例子。Flink 的 Table API 和 SQL,就是典型的流批一体实现。你写一个 SELECT COUNT(*) FROM orders GROUP BY user_id,在批模式下,它会对全量数据做一次聚合;在流模式下,它会维护一个不断更新的计数器。底层用的都是同一套状态后端(比如 RocksDB),只是运行模式不同。

💡 我的建议: 如果你正在选型,优先考虑原生支持流批一体的引擎,比如 Flink。不要再用“流处理引擎 + 批处理引擎”拼凑的方案了。我在项目里吃过这个亏,两套引擎之间的数据同步和语义对齐,比想象中复杂得多。

最后,我用一张图来总结本章的核心逻辑:

流批一体核心逻辑 批处理 全量数据,一次性计算 流处理 增量数据,持续计算 流批一体引擎 统一 API · 统一状态 · 统一语义 核心价值 ✅ 开发效率:一套代码,两种模式 ✅ 数据一致性:共享状态,语义统一 ✅ 运维简化:一套集群,一套监控

嗯,这张图把本章的核心逻辑串起来了。批处理和流处理,通过流批一体引擎统一起来,最终实现开发效率、数据一致性和运维简化三大价值。后面的章节,我们会深入探讨状态复用这个核心机制,看看它到底是怎么实现的。