1. 流式更新概述:为什么需要流式更新、离线与在线特征对比、核心挑战
各位同学,咱们今天聊聊召回特征流式更新。说实话,这个主题我琢磨了好几年。刚做推荐那会儿,我总觉得特征更新嘛,每天跑个离线任务就完事了。直到有一次线上事故,让我彻底改变了看法。
那是个双十一大促的晚上。用户刚点了一个商品,下一秒刷新页面,推荐结果还是老样子。用户明明已经买了某类商品,系统还在拼命推同类。CTR 直接掉了 15%。排查下来,问题就出在特征更新延迟上——离线任务要等 4 小时才能把用户行为刷进特征。4 小时,在推荐系统里,黄花菜都凉了。
所以,流式更新不是锦上添花,是刚需。
1.1 为什么需要流式更新
推荐系统的核心,是捕捉用户兴趣的变化。用户今天喜欢什么,和昨天可能完全不同。你想想看,一个用户刚搜了「婴儿奶粉」,说明家里有新生儿。接下来他可能关注纸尿裤、婴儿车。如果系统还在推游戏机、运动鞋,那体验就太差了。
流式更新的价值,说白了就是两个字:实时。
- 用户行为实时反馈:点击、购买、收藏、加购,这些信号必须在秒级内反映到特征中。
- 物品属性动态变化:库存、价格、标签,这些信息随时在变。我记得有个电商项目,商品价格一天能调 20 次。离线更新根本跟不上。
- 上下文实时感知:时间、地点、天气、网络状态。比如中午 12 点,用户大概率想吃饭。流式更新能让特征带上时间戳,做更精准的推荐。
核心观点:流式更新不是可选项,而是推荐系统从「能用」到「好用」的分水岭。离线更新只能告诉你「用户过去喜欢什么」,流式更新才能告诉你「用户现在想要什么」。
1.2 离线与在线特征对比
咱们来做个对比。离线特征和在线特征,就像「历史课本」和「实时新闻」。各有各的用处,但场景完全不同。
| 对比维度 | 离线特征 | 在线特征(流式) |
|---|---|---|
| 更新频率 | 天级 / 小时级 | 秒级 / 毫秒级 |
| 数据源 | Hive / HDFS 批处理 | Kafka / Pulsar 消息队列 |
| 计算引擎 | Spark / MapReduce | Flink / Spark Streaming |
| 存储介质 | HBase / MySQL / Hive | Redis / 本地缓存 / 内存表 |
| 特征时效性 | 过去 1 天 ~ 30 天 | 过去 1 秒 ~ 1 小时 |
| 计算复杂度 | 高(全量计算) | 低(增量计算) |
| 容错能力 | 强(重跑即可) | 中等(需要 checkpoint) |
| 典型场景 | 用户长期兴趣画像、物品基础属性 | 实时点击序列、价格变动、库存状态 |
我个人的习惯是:离线特征做「骨架」,在线特征做「血肉」。用户的基础画像、物品的静态属性,这些变化慢的东西,离线跑没问题。但用户的实时行为序列、物品的动态状态,必须走流式。
避坑指南:我曾经在一个项目里,把所有特征都做成流式。结果呢?Flink 任务挂了,整个推荐系统直接瘫痪。后来我学乖了——核心的、变化慢的特征,离线兜底;变化快的特征,流式优先。这叫「冷热分离」。
1.3 核心挑战
流式更新听起来很美好,做起来全是坑。我踩过的坑,比走过的路还多。咱们一个一个说。
挑战一:数据一致性
离线任务可以保证「要么全做,要么不做」。流式呢?消息可能丢失、重复、乱序。你想想看,用户点击了商品 A,又点击了商品 B。如果消息乱序,系统可能先处理 B 再处理 A,特征就乱了。
解决方案?幂等性设计 + 事件时间语义。每条消息带上唯一 ID 和时间戳,处理时做去重和排序。Flink 的 watermark 机制就是干这个的。
挑战二:延迟与吞吐的平衡
流式更新,延迟越低越好。但低延迟意味着高成本。每秒钟处理 10 万条消息,还要在 100 毫秒内更新特征,这对系统压力很大。
我记得有个项目,为了追求极致延迟,把特征更新直接写在用户请求链路里。结果呢?接口响应时间从 20ms 涨到 200ms,用户直接骂娘。后来我改成异步更新 + 本地缓存,延迟控制在 50ms 以内,吞吐量还翻了一倍。
挑战三:特征存储的实时读写
离线特征可以存在 HBase 里,批量读写没问题。在线特征呢?要求毫秒级读取,还要支持高频写入。Redis 是首选,但 Redis 也有坑——内存有限,数据过期策略要设计好。
我见过一个团队,把所有特征都塞进 Redis,结果内存爆了,OOM 导致整个缓存集群雪崩。后来他们做了特征分级:热数据放 Redis,温数据放 SSD 本地缓存,冷数据回源 HBase。
挑战四:特征回溯与修复
流式系统一旦出问题,数据就丢了。离线任务可以重跑,流式呢?消息已经消费了,怎么回溯?
我的做法是:流式 + 离线双链路。流式负责实时更新,离线负责天级全量修复。每天凌晨,离线任务把当天的特征重新算一遍,覆盖流式可能产生的错误。这叫「流批一体」的降级方案。
注意:流式更新不是银弹。如果你的业务场景对实时性要求不高(比如用户画像每天更新一次就够了),别盲目上流式。流式系统的运维成本,比离线高一个数量级。
1.4 流式更新整体架构
说了这么多,咱们来看看流式更新的整体架构长什么样。下面这张图,是我在多个项目中沉淀下来的通用方案。
这个架构,我在三个项目里验证过。核心思路就是:消息队列解耦 + 流式计算处理 + 高速缓存存储。每一层都有明确的职责,出了问题也好排查。
1.5 本章小结
咱们这一章,主要讲了三个东西:
- 为什么需要流式更新:用户行为实时反馈、物品属性动态变化、上下文实时感知。说白了,就是让推荐系统跟上用户的变化节奏。
- 离线与在线特征对比:离线特征适合变化慢、计算复杂的场景;在线特征适合变化快、要求低延迟的场景。两者互补,不是替代关系。
- 核心挑战:数据一致性、延迟与吞吐平衡、存储实时读写、特征回溯修复。每个挑战都有对应的解决方案,后面几章会详细展开。
一句话总结:流式更新是推荐系统从「离线时代」迈向「实时时代」的关键一步。但这一步,需要扎实的架构设计和性能优化功底。接下来的 29 章,我会带着大家把每个环节都拆开揉碎,讲清楚、说明白。