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架构的核心思路:

  1. 所有数据都进入Kafka(或类似的消息队列),永久保存。
  2. 流处理作业从Kafka的起始位置开始消费,重新计算所有特征。
  3. 如果需要修正逻辑,就启动一个新的流作业,从源头重跑。
  4. 新作业追上进度后,切换流量,下线旧作业。

说白了,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 特征生产管道的核心流程

不管用哪种架构,特征生产管道都逃不开这几个步骤。我画了一张图,帮你理清整体脉络。

特征生产管道核心流程 数据源 DB / 日志 / API 消息队列 Kafka / Pulsar 批处理层 Spark / Hive 流处理层 Flink / Kafka Streams 特征存储 Redis / Cassandra 在线服务 关键设计要点 • 批流共用特征计算逻辑 • 特征存储统一读写接口 • 监控管道延迟和数据质量

这张图展示了特征生产管道的完整链路。数据从源头进来,经过消息队列分流,批处理和流处理并行计算,最终汇入特征存储,供在线服务使用。

嗯,这里要注意一个细节:特征存储是统一出口。不管批处理还是流处理算出来的特征,最终都要写到同一个存储里,用同一个Schema。我见过最惨的案例,批处理写Redis的key格式是 user:123:click_7d,流处理写的是 user_123_click_7d,线上查询直接404。

4.6 实战中的避坑清单

最后,我把自己踩过的坑整理成一份清单。你照着检查,能省不少调试时间。

  1. 时间窗口对齐:批处理和流处理的窗口边界必须一致。比如「过去7天」,批处理从00:00:00算,流处理也得从00:00:00算。
  2. 幂等性设计:批处理重跑时,不能产生重复数据。用覆盖写(UPSERT)而不是追加写。
  3. 状态大小监控:流处理的状态会随时间增长。我习惯设置状态大小告警,超过阈值自动触发checkpoint。
  4. 数据延迟处理:流处理中,迟到数据(late data)要单独处理。Flink的allowedLateness参数要设对。
  5. 特征版本管理:每次修改特征逻辑,都要生成新版本。线上查询指定版本,避免新旧混用。

特征生产管道这件事,没有银弹。Lambda架构稳但重,Kappa架构轻但挑场景。我的建议是:先搞清楚你的业务到底需要多快的特征,再决定用哪种架构。别为了炫技上Kappa,结果历史数据重放跑了一周还没跑完。