流批一体架构基础:从Lambda到Kappa,再到真正的统一
聊数据质量之前,得先把架构这事掰扯清楚。我见过太多团队,质量规则写得挺漂亮,结果一上线发现架构根本撑不住——批处理跑得好好的,流处理一来全乱套。说白了,流批一体不是把两套东西硬塞到一起,而是从根上重新设计。
这一节,咱们就聊聊架构的演变、核心思想,以及我这些年用下来的技术选型心得。
Lambda架构:经典但有点“拧巴”
Lambda架构是Nathan Marz提出来的,当年为了解决大数据处理的实时性和完整性。它的核心思路很简单:
- 批处理层:处理全量历史数据,保证最终一致性,产出准确结果
- 流处理层:处理实时增量数据,提供低延迟的近似结果
- 服务层:合并两边的结果,对外提供服务
听起来挺合理对吧?但我实际用下来,发现几个让人头疼的问题。
Lambda架构最大的问题,说白了就是维护两套代码。同样的业务逻辑,批处理写一套,流处理再写一套,出点bug两边都得修。而且两套结果天然存在差异,你得花大量精力去做“合并逻辑”和“偏差修正”。
| 维度 | 批处理层 | 流处理层 |
|---|---|---|
| 延迟 | 高(分钟~小时) | 低(秒级) |
| 准确性 | 精确 | 近似(受窗口、去重算法影响) |
| 代码维护 | 一套逻辑 | 另一套逻辑 |
| 存储 | HDFS/Hive | Kafka/内存 |
Kappa架构:简化,但没那么简单
后来Jay Kreps(Kafka的作者)提出了Kappa架构。他的想法很直接:既然流处理引擎也能处理历史数据,为什么还要两套?
Kappa架构的核心就一条:所有数据都走流处理管道。历史数据通过重放Kafka里的日志来重新计算,实时数据直接进入流处理。这样你只需要维护一套代码。
嗯,理想很丰满。但我在实践中发现,Kappa架构有几个前提条件:
- 你的流处理引擎必须能处理全量历史数据,不能只做增量
- Kafka的数据保留期要足够长,或者你有能力从外部存储重新灌入
- 你的业务场景允许一定的回溯计算延迟
流批一体核心思想:同一份数据,同一套逻辑
流批一体不是Lambda和Kappa的简单二选一。它的核心思想,我总结成三句话:
- 同一份数据:流和批共享同一份存储,不搞两套数据副本
- 同一套逻辑:同一个算子、同一个SQL,既能跑流也能跑批
- 同一个结果:流处理的结果和批处理的结果,最终必须一致
你想想看,如果做到了这三点,数据质量的问题就简单多了——你只需要在一套逻辑里定义质量规则,流和批自然都遵守。
流批一体的本质: 不是“流+批”,而是“流=批”。批处理是流处理在有限数据集上的特例。
为什么会这样?因为流处理引擎(比如Flink)内部把无限流切成了一个个有限窗口,每个窗口本质上就是一个微批。所以从计算模型上看,流和批是统一的。
主流技术选型:Flink、Spark、Kafka怎么搭?
技术选型这事,我个人的习惯是:先看场景,再选工具,别跟风。下面说说我对这三个核心组件的看法。
Apache Flink:流批一体的“原住民”
Flink从诞生起就是为流处理设计的。它的核心优势在于:
- 真正的流处理:每条数据逐条处理,延迟极低(毫秒级)
- 事件时间语义:支持乱序数据,watermark机制成熟
- 批流一体:同一个DataStream API,批处理只是流处理的有限模式
- 状态管理:内置RocksDB状态后端,支持超大状态
我在做实时数仓项目时,Flink是首选。尤其是需要精确一次语义的场景,Flink的checkpoint机制非常可靠。
// Flink 流批一体的典型写法:同一个SQL,流批都能跑
CREATE TABLE orders (
order_id BIGINT,
user_id BIGINT,
amount DECIMAL(10, 2),
ts TIMESTAMP(3),
WATERMARK FOR ts AS ts - INTERVAL '5' SECOND
) WITH (
'connector' = 'kafka', -- 流模式用kafka
'topic' = 'orders',
'properties.bootstrap.servers' = 'localhost:9092',
'format' = 'json'
);
-- 同样的SQL,换成filesystem connector就是批处理
-- 'connector' = 'filesystem', 'path' = '/data/orders'
Apache Spark:批处理的王者,流处理也能打
Spark的流处理(Structured Streaming)是基于微批的。它把流数据切成小批次,然后用Spark SQL的批处理引擎去算。优点是:
- 生态丰富:MLlib、GraphX、Spark SQL全家桶
- 批处理性能强:尤其是大规模离线ETL
- 学习成本低:会写Spark SQL就能做流处理
但缺点也很明显:微批模式延迟较高(秒级),而且处理乱序数据时不如Flink灵活。
Apache Kafka:流批一体的“数据总线”
Kafka在流批一体架构里的角色,不只是消息队列。它是数据的统一存储层。
- 数据持久化:Kafka的日志可以保留几天甚至几周,支持回溯消费
- 多消费者:同一份数据,Flink消费做实时,Spark消费做批处理
- Exactly-Once语义:配合Flink或Kafka Streams,保证数据不丢不重
我个人习惯把Kafka当作“数据的”。所有源数据先进Kafka,然后Flink和Spark各取所需。这样数据链路清晰,也方便做数据质量监控——在Kafka这一层就可以做schema校验和异常检测。
架构对比总结
| 维度 | Lambda架构 | Kappa架构 | 流批一体(Flink原生) |
|---|---|---|---|
| 代码维护 | 两套 | 一套 | 一套 |
| 延迟 | 流低批高 | 低 | 低 |
| 准确性 | 批精确流近似 | 精确(需重放) | 精确(事件时间+状态) |
| 适用场景 | 传统数仓、报表 | 实时管道、风控 | 实时数仓、数据湖 |
| 数据质量难度 | 高(两套结果对齐) | 中(重放一致性) | 低(统一逻辑) |
一张图看懂流批一体架构
下面这张图是我自己总结的流批一体核心架构。你看,数据从源端进入Kafka,Flink同时处理实时流和历史数据,Spark负责大规模批处理ETL,最终统一写入数据湖或数仓。质量监控贯穿全链路。
我的选型建议:
- 如果团队从零开始,且实时需求强,直接上Flink + Kafka
- 如果团队已有Spark生态,用Structured Streaming过渡,逐步引入Flink
- 如果数据质量要求极高(金融、医疗),优先Flink的精确一次语义
- Kafka是必选组件,别省。它不仅是消息队列,更是数据质量的“第一道防线”