数据管道架构:批处理与流处理、Lambda架构、Kappa架构
聊到特征新鲜度,就绕不开数据管道的架构选型。说白了,你用什么方式把原始数据变成特征,决定了你的特征到底有多「新鲜」。我见过不少团队,特征工程做得花里胡哨,结果管道架构没选对,特征延迟几个小时才更新——那前面再好的算法也白搭。
这一节,咱们把批处理、流处理、Lambda 和 Kappa 这几种架构掰开揉碎讲清楚。嗯,都是实战中反复踩坑总结出来的经验。
批处理 vs 流处理:两种思维模式
先搞清楚这两个基础概念。它们不是谁替代谁的关系,而是解决不同问题的工具。
| 维度 | 批处理 | 流处理 |
|---|---|---|
| 数据范围 | 有界数据集(过去一段时间的数据) | 无界数据流(持续到达的数据) |
| 延迟 | 分钟级到小时级 | 毫秒级到秒级 |
| 计算模型 | MapReduce、Spark Batch | Flink、Kafka Streams、Spark Streaming |
| 典型场景 | 离线特征、历史回溯、全量计算 | 实时特征、在线预测、监控告警 |
| 容错方式 | 重跑整个批次 | Checkpoint + 状态恢复 |
我个人习惯这样区分:批处理是「回头看」,把过去一小时、一天的数据拉出来算一遍;流处理是「边看边算」,数据一来就处理,不等人。
Lambda 架构:批流混合的经典方案
Lambda 架构是 Nathan Marz 提出来的。它的核心思想很简单:用批处理保证准确性,用流处理保证低延迟。两条路径各自算,最后合并结果。
结构上分三层:
- 批处理层(Batch Layer):存储全量原始数据,定期重算所有特征。结果准确,但延迟高。
- 速度层(Speed Layer):实时处理新到的数据,补偿批处理的延迟。结果有近似,但延迟低。
- 服务层(Serving Layer):合并批处理结果和速度层结果,对外提供统一查询。
听起来挺完美对吧?但我在项目中遇到过它的痛点——维护两套代码。批处理写一套 Spark,流处理再写一套 Flink,逻辑一样,实现不同。一旦特征逻辑变了,两边都得改,还容易改出不一致。
下面这张图展示了 Lambda 架构的核心流程:
Kappa 架构:简化,再简化
Kappa 架构是 Jay Kreps 提出来的。他当时在 LinkedIn 做 Kafka,觉得 Lambda 太复杂了——为什么非要维护两套逻辑?
Kappa 的核心思想:只用一套流处理引擎,处理所有数据。历史数据通过「重放」的方式,从 Kafka 等消息队列里重新消费一遍,相当于用流处理模拟批处理。
你想想看,这样一来,代码只有一套,逻辑完全一致。特征逻辑变了?把数据从源头重新消费一遍就行。
但 Kappa 也不是银弹。我在项目中遇到过几个问题:
- 历史数据重放耗时:如果数据量很大,重放几天甚至几周的数据,流处理引擎可能扛不住。
- 状态管理复杂:流处理的状态大小受限于内存,全量重算时容易 OOM。
- 消息队列存储成本:Kafka 保存全量历史数据,磁盘开销不小。
下面这张图对比了两种架构的差异:
怎么选?我的经验法则
说实话,没有正确的架构,只有适合你场景的架构。我这些年总结了几条判断标准:
- 特征新鲜度要求 < 1 分钟:必须上流处理,Lambda 或 Kappa 都行。如果团队流处理经验丰富,优先 Kappa。
- 特征新鲜度要求 1-10 分钟:Lambda 架构的批处理层可以用微批次(比如 5 分钟一个批次),速度层压力小很多。
- 特征新鲜度要求 > 10 分钟:纯批处理就够了,别折腾流处理。我见过有人用批处理做小时级特征,非要在中间加一层 Flink,纯属给自己找麻烦。
- 历史数据回溯需求频繁:Lambda 架构的批处理层天然适合全量重算,Kappa 的重放机制在这种场景下反而吃力。
最后说一句:架构选型只是第一步。真正决定特征新鲜度的,是你在管道里怎么处理窗口、怎么管理状态、怎么保证 Exactly-Once 语义。这些内容,咱们后面几节慢慢聊。