数据管道架构:批处理与流处理、Lambda架构、Kappa架构

聊到特征新鲜度,就绕不开数据管道的架构选型。说白了,你用什么方式把原始数据变成特征,决定了你的特征到底有多「新鲜」。我见过不少团队,特征工程做得花里胡哨,结果管道架构没选对,特征延迟几个小时才更新——那前面再好的算法也白搭。

这一节,咱们把批处理、流处理、Lambda 和 Kappa 这几种架构掰开揉碎讲清楚。嗯,都是实战中反复踩坑总结出来的经验。

批处理 vs 流处理:两种思维模式

先搞清楚这两个基础概念。它们不是谁替代谁的关系,而是解决不同问题的工具。

维度 批处理 流处理
数据范围 有界数据集(过去一段时间的数据) 无界数据流(持续到达的数据)
延迟 分钟级到小时级 毫秒级到秒级
计算模型 MapReduce、Spark Batch Flink、Kafka Streams、Spark Streaming
典型场景 离线特征、历史回溯、全量计算 实时特征、在线预测、监控告警
容错方式 重跑整个批次 Checkpoint + 状态恢复

我个人习惯这样区分:批处理是「回头看」,把过去一小时、一天的数据拉出来算一遍;流处理是「边看边算」,数据一来就处理,不等人。

一个小建议:别一上来就追求纯流处理。我见过太多团队,业务需求明明是小时级更新,非要上 Flink,结果运维成本翻了好几倍。先搞清楚你的特征新鲜度要求到底是多少。

Lambda 架构:批流混合的经典方案

Lambda 架构是 Nathan Marz 提出来的。它的核心思想很简单:用批处理保证准确性,用流处理保证低延迟。两条路径各自算,最后合并结果。

结构上分三层:

  • 批处理层(Batch Layer):存储全量原始数据,定期重算所有特征。结果准确,但延迟高。
  • 速度层(Speed Layer):实时处理新到的数据,补偿批处理的延迟。结果有近似,但延迟低。
  • 服务层(Serving Layer):合并批处理结果和速度层结果,对外提供统一查询。

听起来挺完美对吧?但我在项目中遇到过它的痛点——维护两套代码。批处理写一套 Spark,流处理再写一套 Flink,逻辑一样,实现不同。一旦特征逻辑变了,两边都得改,还容易改出不一致。

避坑指南:我曾经在一个推荐系统项目里,批处理和流处理的窗口聚合逻辑差了 5 分钟对齐方式,结果线上特征和离线特征对不上,排查了整整两天。Lambda 架构的「双写」问题,是真实存在的坑。

下面这张图展示了 Lambda 架构的核心流程:

Lambda 架构核心流程 原始数据流 批处理层 全量数据 · 定期重算 · 高准确 速度层 实时数据 · 低延迟 · 近似结果 批处理视图 历史特征 · 全量准确 实时视图 增量特征 · 近实时 合并查询结果

Kappa 架构:简化,再简化

Kappa 架构是 Jay Kreps 提出来的。他当时在 LinkedIn 做 Kafka,觉得 Lambda 太复杂了——为什么非要维护两套逻辑?

Kappa 的核心思想:只用一套流处理引擎,处理所有数据。历史数据通过「重放」的方式,从 Kafka 等消息队列里重新消费一遍,相当于用流处理模拟批处理。

你想想看,这样一来,代码只有一套,逻辑完全一致。特征逻辑变了?把数据从源头重新消费一遍就行。

关键区别:Lambda 是「批 + 流」两条腿走路,Kappa 是「一条流走到底」。Kappa 的假设是:你的流处理引擎能处理历史数据重放,且消息队列能保存足够长的历史数据。

但 Kappa 也不是银弹。我在项目中遇到过几个问题:

  • 历史数据重放耗时:如果数据量很大,重放几天甚至几周的数据,流处理引擎可能扛不住。
  • 状态管理复杂:流处理的状态大小受限于内存,全量重算时容易 OOM。
  • 消息队列存储成本:Kafka 保存全量历史数据,磁盘开销不小。

下面这张图对比了两种架构的差异:

Lambda vs Kappa 架构对比 Lambda 架构 批处理代码 流处理代码 两套代码,逻辑需对齐 批处理保证准确性 流处理保证低延迟 服务层合并结果 ⚠ 维护成本高,易出现不一致 Kappa 架构 一套流处理代码 历史数据通过重放处理 逻辑完全统一 无需合并层 架构更简洁 ⚠ 重放耗时,状态管理挑战大

怎么选?我的经验法则

说实话,没有正确的架构,只有适合你场景的架构。我这些年总结了几条判断标准:

  1. 特征新鲜度要求 < 1 分钟:必须上流处理,Lambda 或 Kappa 都行。如果团队流处理经验丰富,优先 Kappa。
  2. 特征新鲜度要求 1-10 分钟:Lambda 架构的批处理层可以用微批次(比如 5 分钟一个批次),速度层压力小很多。
  3. 特征新鲜度要求 > 10 分钟:纯批处理就够了,别折腾流处理。我见过有人用批处理做小时级特征,非要在中间加一层 Flink,纯属给自己找麻烦。
  4. 历史数据回溯需求频繁:Lambda 架构的批处理层天然适合全量重算,Kappa 的重放机制在这种场景下反而吃力。
我的个人习惯:新项目起步时,先用 Lambda 架构把批处理层搭好,保证数据准确性。等流处理经验积累够了,再逐步往 Kappa 迁移。别一上来就追求「最先进」的架构,稳扎稳打更重要。

最后说一句:架构选型只是第一步。真正决定特征新鲜度的,是你在管道里怎么处理窗口、怎么管理状态、怎么保证 Exactly-Once 语义。这些内容,咱们后面几节慢慢聊。