4、特征生产管道:批处理与流处理、Lambda架构与Kappa架构
特征生产管道,说白了就是「怎么把原始数据变成模型能用的特征」。这件事听起来简单,做起来全是坑。我这些年见过太多团队,特征管道写得像意大利面条,跑起来三天两头断,一查原因——批处理和流处理混在一起,逻辑乱成一锅粥。
今天咱们就把这事掰扯清楚。先看两种最基本的处理模式,再聊两个经典架构。
4.1 批处理与流处理:两种节奏
批处理和流处理,本质区别就一句话:数据是攒够了再算,还是来一条算一条。
| 维度 | 批处理 | 流处理 |
|---|---|---|
| 触发方式 | 定时调度(每小时/每天) | 事件到达即触发 |
| 延迟 | 分钟级~天级 | 毫秒级~秒级 |
| 吞吐量 | 极高,适合全量计算 | 中等,适合增量计算 |
| 容错 | 重跑即可,天然幂等 | 需要状态管理和精确一次语义 |
| 典型工具 | Spark SQL、Hive、MapReduce | Flink、Kafka Streams、Spark Streaming |
批处理适合什么场景?用户画像、离线报表、模型训练样本。这些任务不要求实时,但要求数据完整、计算准确。我习惯把批处理当作「地基」——先把历史数据算清楚,后面流处理才能站得稳。
流处理适合什么场景?实时风控、在线推荐、动态定价。用户点了一下,你得在几百毫秒内算出特征,推给模型,返回结果。这时候批处理就太慢了。
核心原则:批处理保证「全」和「准」,流处理保证「快」和「新」。两者不是替代关系,是互补关系。
4.2 Lambda架构:批+流两条腿走路
Lambda架构是Nathan Marz提出来的,思路很直白:同一套特征逻辑,跑两遍。
- 批处理层:全量计算,产出完整准确的特征。延迟高,但结果可靠。
- 流处理层:增量计算,产出近实时的特征。延迟低,但可能有误差。
- 服务层:把两边的结果合并,对外提供统一查询。
我举个例子。假设你要算「用户过去7天点击次数」这个特征。
- 批处理层:每天凌晨跑一次,从原始日志里算全量数据,存到特征存储里。
- 流处理层:实时消费Kafka里的点击事件,增量更新当天的点击次数。
- 服务层:查询时,取「昨天之前的全量数据 + 今天的增量数据」合并返回。
听起来挺合理对吧?但我在项目中遇到过一个大坑——两套代码,两套逻辑,对不齐。
避坑指南:我曾经在一个推荐系统项目里,批处理和流处理分别由两个团队开发。上线后发现,同一个特征,批处理算出来是100,流处理算出来是98。排查了三天,发现是窗口边界定义差了1秒。从那以后,我要求批处理和流处理必须共用同一套特征计算逻辑,用同一份配置。
Lambda架构的优点是成熟、稳定、容错好。缺点是维护成本高——你得同时维护两套管道,还要处理合并时的数据不一致。
4.3 Kappa架构:只用流处理一条腿
Kappa架构是Jay Kreps提出来的。他的观点很激进:既然流处理框架(比如Kafka)已经能保存全量数据,为什么还要批处理?
Kappa架构的核心思路:
- 所有数据都进入Kafka(或类似的消息队列),永久保存。
- 流处理作业从Kafka的起始位置开始消费,重新计算所有特征。
- 如果需要修正逻辑,就启动一个新的流作业,从源头重跑。
- 新作业追上进度后,切换流量,下线旧作业。
说白了,Kappa架构把「批处理」变成了「流处理的重放」。你想想看,Kafka里数据是持久化的,流处理框架(比如Flink)支持状态快照和精确一次语义,理论上确实不需要批处理了。
我的经验:Kappa架构在中小规模场景下非常好用。我做过一个实时特征平台,每天处理500亿条事件,用Flink + Kafka,一套代码搞定批和流。维护成本比Lambda低一半。
但Kappa架构也有硬伤:
- 重放成本高:如果历史数据量巨大(比如PB级),从Kafka重放一遍可能要跑好几天。
- 状态管理复杂:流处理的状态大小受限于内存,大状态容易OOM。
- 回溯困难:如果需要修正3个月前的特征,Kafka可能已经清理了旧数据。
4.4 架构对比与选型建议
| 维度 | Lambda架构 | Kappa架构 |
|---|---|---|
| 代码维护 | 两套代码,容易不一致 | 一套代码,逻辑统一 |
| 数据一致性 | 需要合并逻辑,容易出错 | 天然一致(同一套逻辑) |
| 历史回溯 | 容易,批处理重跑即可 | 困难,依赖Kafka保留时长 |
| 延迟 | 批处理延迟高,流处理延迟低 | 统一低延迟 |
| 资源消耗 | 高(两套管道并行) | 中等(一套管道) |
| 适用场景 | 大公司、复杂业务、强一致性要求 | 中小团队、快速迭代、数据量可控 |
我个人习惯这样选型:
- 如果团队规模大、业务复杂、历史数据量巨大 → Lambda架构。稳字当头。
- 如果团队小、迭代快、数据量在百TB以内 → Kappa架构。省心省力。
- 如果两者都不满意 → 可以考虑混合架构:核心特征用Lambda,非核心特征用Kappa。
4.5 特征生产管道的核心流程
不管用哪种架构,特征生产管道都逃不开这几个步骤。我画了一张图,帮你理清整体脉络。
这张图展示了特征生产管道的完整链路。数据从源头进来,经过消息队列分流,批处理和流处理并行计算,最终汇入特征存储,供在线服务使用。
嗯,这里要注意一个细节:特征存储是统一出口。不管批处理还是流处理算出来的特征,最终都要写到同一个存储里,用同一个Schema。我见过最惨的案例,批处理写Redis的key格式是 user:123:click_7d,流处理写的是 user_123_click_7d,线上查询直接404。
4.6 实战中的避坑清单
最后,我把自己踩过的坑整理成一份清单。你照着检查,能省不少调试时间。
- 时间窗口对齐:批处理和流处理的窗口边界必须一致。比如「过去7天」,批处理从00:00:00算,流处理也得从00:00:00算。
- 幂等性设计:批处理重跑时,不能产生重复数据。用覆盖写(UPSERT)而不是追加写。
- 状态大小监控:流处理的状态会随时间增长。我习惯设置状态大小告警,超过阈值自动触发checkpoint。
- 数据延迟处理:流处理中,迟到数据(late data)要单独处理。Flink的allowedLateness参数要设对。
- 特征版本管理:每次修改特征逻辑,都要生成新版本。线上查询指定版本,避免新旧混用。
特征生产管道这件事,没有银弹。Lambda架构稳但重,Kappa架构轻但挑场景。我的建议是:先搞清楚你的业务到底需要多快的特征,再决定用哪种架构。别为了炫技上Kappa,结果历史数据重放跑了一周还没跑完。