流批一体架构基础:从Lambda到Kappa,再到真正的统一

聊数据质量之前,得先把架构这事掰扯清楚。我见过太多团队,质量规则写得挺漂亮,结果一上线发现架构根本撑不住——批处理跑得好好的,流处理一来全乱套。说白了,流批一体不是把两套东西硬塞到一起,而是从根上重新设计。

这一节,咱们就聊聊架构的演变、核心思想,以及我这些年用下来的技术选型心得。

Lambda架构:经典但有点“拧巴”

Lambda架构是Nathan Marz提出来的,当年为了解决大数据处理的实时性和完整性。它的核心思路很简单:

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

听起来挺合理对吧?但我实际用下来,发现几个让人头疼的问题。

我踩过的坑: 曾经在一个电商实时大屏项目里,批处理层用Hive跑T+1的聚合,流处理层用Storm算实时UV。结果两个结果对不上——批处理那边去重逻辑更严谨,流处理那边为了性能做了近似。最后服务层合并的时候,数据对不齐,业务方天天来问“哪个数是对的?”。

Lambda架构最大的问题,说白了就是维护两套代码。同样的业务逻辑,批处理写一套,流处理再写一套,出点bug两边都得修。而且两套结果天然存在差异,你得花大量精力去做“合并逻辑”和“偏差修正”。

维度 批处理层 流处理层
延迟 高(分钟~小时) 低(秒级)
准确性 精确 近似(受窗口、去重算法影响)
代码维护 一套逻辑 另一套逻辑
存储 HDFS/Hive Kafka/内存

Kappa架构:简化,但没那么简单

后来Jay Kreps(Kafka的作者)提出了Kappa架构。他的想法很直接:既然流处理引擎也能处理历史数据,为什么还要两套?

Kappa架构的核心就一条:所有数据都走流处理管道。历史数据通过重放Kafka里的日志来重新计算,实时数据直接进入流处理。这样你只需要维护一套代码。

嗯,理想很丰满。但我在实践中发现,Kappa架构有几个前提条件:

  • 你的流处理引擎必须能处理全量历史数据,不能只做增量
  • Kafka的数据保留期要足够长,或者你有能力从外部存储重新灌入
  • 你的业务场景允许一定的回溯计算延迟
我的建议: 如果你做的是实时风控、实时推荐这类场景,Kappa架构很合适。但如果你要做T+1的财务对账、监管报表,Lambda架构的批处理层反而更稳。别为了“架构先进”而硬上Kappa。

流批一体核心思想:同一份数据,同一套逻辑

流批一体不是Lambda和Kappa的简单二选一。它的核心思想,我总结成三句话:

  1. 同一份数据:流和批共享同一份存储,不搞两套数据副本
  2. 同一套逻辑:同一个算子、同一个SQL,既能跑流也能跑批
  3. 同一个结果:流处理的结果和批处理的结果,最终必须一致

你想想看,如果做到了这三点,数据质量的问题就简单多了——你只需要在一套逻辑里定义质量规则,流和批自然都遵守。

流批一体的本质: 不是“流+批”,而是“流=批”。批处理是流处理在有限数据集上的特例。

为什么会这样?因为流处理引擎(比如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灵活。

我曾经踩过的坑: 用Spark Structured Streaming做实时去重,用的dropDuplicates("userId", "eventTime")。结果因为微批的触发间隔设置不合理,导致去重窗口内的数据不完整,重复率飙升。后来改成Flink的Deduplication + TTL状态才解决。

Apache Kafka:流批一体的“数据总线”

Kafka在流批一体架构里的角色,不只是消息队列。它是数据的统一存储层。

  • 数据持久化:Kafka的日志可以保留几天甚至几周,支持回溯消费
  • 多消费者:同一份数据,Flink消费做实时,Spark消费做批处理
  • Exactly-Once语义:配合Flink或Kafka Streams,保证数据不丢不重

我个人习惯把Kafka当作“数据的”。所有源数据先进Kafka,然后Flink和Spark各取所需。这样数据链路清晰,也方便做数据质量监控——在Kafka这一层就可以做schema校验和异常检测。

架构对比总结

维度 Lambda架构 Kappa架构 流批一体(Flink原生)
代码维护 两套 一套 一套
延迟 流低批高 低 低
准确性 批精确流近似 精确(需重放) 精确(事件时间+状态)
适用场景 传统数仓、报表 实时管道、风控 实时数仓、数据湖
数据质量难度 高(两套结果对齐) 中(重放一致性) 低(统一逻辑)

一张图看懂流批一体架构

下面这张图是我自己总结的流批一体核心架构。你看,数据从源端进入Kafka,Flink同时处理实时流和历史数据,Spark负责大规模批处理ETL,最终统一写入数据湖或数仓。质量监控贯穿全链路。

流批一体核心架构图 流批一体核心架构 数据源 App / DB / IoT / 日志 Apache Kafka 统一数据总线 Apache Flink 流处理 + 批处理 事件时间 / 状态管理 Apache Spark 批量ETL / 离线分析 微批 / Structured Streaming 数据湖 / 数仓 Iceberg / Hudi / Delta 数据质量监控 Schema校验 / 异常检测 质量规则统一管理 核心原则 同一份数据 · 同一套逻辑 · 同一个结果

我的选型建议:

  • 如果团队从零开始,且实时需求强,直接上Flink + Kafka
  • 如果团队已有Spark生态,用Structured Streaming过渡,逐步引入Flink
  • 如果数据质量要求极高(金融、医疗),优先Flink的精确一次语义
  • Kafka是必选组件,别省。它不仅是消息队列,更是数据质量的“第一道防线”