4. 实时特征系统架构

实时特征系统是支撑毫秒级特征计算与在线推理的核心基础设施。与离线批处理不同,实时特征要求数据从产生到特征就绪的端到端延迟控制在秒级甚至毫秒级,同时保证高可用与数据一致性。本节从流处理架构选型、实时特征存储、实时特征计算流程三个维度展开。

4.1 流处理架构:Lambda 与 Kappa

实时特征系统的流处理层通常采用两种经典架构:Lambda 架构与Kappa 架构。两者在数据路径、复杂度与一致性保障上存在显著差异。

架构选型核心原则: 如果业务同时依赖精确的历史回溯与低延迟实时特征,且团队有能力维护两条独立管道,Lambda 架构更稳妥;如果业务可以容忍实时层少量数据修正,且希望降低运维复杂度,Kappa 架构是更现代的选择。

4.1.1 Lambda 架构的流处理层

Lambda 架构将数据管道分为批处理层(Batch Layer)与流处理层(Speed Layer)。在实时特征场景中,流处理层负责处理近实时的增量数据,提供低延迟特征输出;批处理层则定期重算全量历史数据,修正流处理层可能产生的偏差。

维度 批处理层 流处理层
数据范围 全量历史数据 最近窗口增量数据
延迟要求 分钟级~小时级 秒级~毫秒级
计算引擎 Spark / Hive / MapReduce Flink / Kafka Streams / Storm
特征准确性 精确(全量重算) 近似(可能因乱序或延迟丢失)
存储输出 离线特征表(Hive / HBase) 实时特征存储(Redis / 内存网格)
典型场景 日级特征回溯、模型训练样本生成 在线推理、实时风控、实时推荐

Lambda 架构的流处理层通常采用 Flink 或 Kafka Streams 作为核心引擎,消费 Kafka 中的实时事件流,执行窗口聚合、状态计算、特征变换后,将结果写入实时特征存储。批处理层则定期(如每 6 小时或每天)重跑全量特征计算,覆盖流处理层可能出现的误差。

Lambda 架构的运维挑战: 两条独立管道意味着需要维护两套代码、两套部署、两套监控。当流处理层与批处理层计算结果不一致时,排查成本较高。建议在流处理层与批处理层之间建立统一的特征计算逻辑抽象层,避免同一特征在不同管道中产生语义偏差。

4.1.2 Kappa 架构

Kappa 架构由 LinkedIn 的 Jay Kreps 提出,核心思想是只用一套流处理管道处理所有数据。所有数据(包括历史数据)都被视为持续到达的事件流,通过重放 Kafka 等消息队列中的历史消息,实现与批处理等效的全量计算。

  • 统一数据入口: 所有数据(实时事件 + 历史数据)都写入 Kafka,流处理引擎统一消费。
  • 重放机制: 当需要修正特征逻辑或回溯历史特征时,只需重置消费偏移量,从指定时间点重新消费并计算。
  • 状态管理: 依赖 Flink 的 Checkpoint / Savepoint 机制或 Kafka Streams 的状态存储,保证计算状态的可恢复性。
  • 存储层解耦: 实时特征结果写入 Redis / 内存网格,历史特征结果写入 HBase / Cassandra,但计算逻辑完全复用。
Kappa 架构适用条件: 消息队列(Kafka)的吞吐量与保留时长足够支撑全量数据重放;流处理引擎的状态后端(RocksDB / HDFS)能够容纳全量特征状态;业务对实时特征与历史特征的一致性要求可以通过重放机制满足,而非必须依赖离线批处理修正。

下表对比 Lambda 与 Kappa 在实时特征场景下的关键差异:

对比维度 Lambda 架构 Kappa 架构
管道数量 2 条(批 + 流) 1 条(纯流)
代码维护 两套代码,需对齐逻辑 一套代码,复用性强
历史回溯 批处理层重算 Kafka 重放 + 状态恢复
数据一致性 批处理层最终修正 重放保证最终一致
运维复杂度 高(双管道监控、双部署) 中(单管道,依赖 Kafka 容量)
实时特征延迟 秒级(流处理层) 秒级(纯流处理)
典型选型场景 金融风控、广告计费(强一致性) 实时推荐、用户画像(可容忍短暂偏差)
Lambda 与 Kappa 架构对比图 Lambda 架构 实时事件流 流处理层 (Flink) 实时特征存储 全量历史数据 批处理层 (Spark) 离线特征表 延迟:秒级 延迟:分钟~小时级 Kappa 架构 统一事件流 (Kafka) 流处理引擎 (Flink) 实时特征存储 延迟:秒级 历史重放:重置偏移量 核心差异:Lambda 用批处理修正实时偏差,Kappa 用重放机制保证最终一致性 选型建议:强一致性场景选 Lambda,可容忍短暂偏差且追求运维简化选 Kappa

4.2 实时特征存储:Redis 与内存网格

实时特征存储是连接流处理引擎与在线推理服务的桥梁。特征计算完成后,需要以极低延迟(微秒~毫秒级)被在线服务读取。传统关系型数据库无法满足这一性能要求,业界主流方案是 Redis 与内存网格(如 Hazelcast、Apache Ignite)。

4.2.1 Redis 作为实时特征存储

Redis 是实时特征存储的首选方案,其全内存数据结构、丰富的数据类型、主从复制与集群模式,使其天然适合高频读写、低延迟的特征存取场景。

特征类型 推荐 Redis 数据结构 Key 设计示例 说明
用户实时统计特征 Hash feature:user:{userId} 存储用户近 1 小时/1 天的点击、曝光、转化计数
物品实时属性 String / Hash item:{itemId}:price 存储物品实时价格、库存、状态
滑动窗口聚合 Sorted Set window:click:{userId}:1h 以时间戳为 score,存储窗口内事件,支持范围查询
实时 TopK 特征 Sorted Set topk:hot_items 存储实时热门物品排名,score 为热度值
布隆过滤器(去重) Bloom Filter(Redis 模块) bloom:impression:{userId} 判断用户是否已曝光某物品,避免重复推荐
特征版本控制 String + 过期时间 feature:version:user_click 存储特征计算版本号,配合 TTL 自动过期
Redis 特征存储最佳实践:
  • 为 Key 添加业务前缀与命名空间,如 feature:user:{userId}:click_cnt_1h,避免 Key 冲突。
  • 合理设置 TTL(过期时间),防止无效特征长期占用内存。实时特征通常设置 1 小时~24 小时 TTL。
  • 使用 Pipeline 或 Lua 脚本批量读写,减少网络往返次数。
  • 对于高并发场景,启用 Redis Cluster 或 Proxy 分片,避免单点瓶颈。

4.2.2 内存网格(Hazelcast / Apache Ignite)

当特征存储需要更强的计算推送能力(如分布式聚合、SQL 查询、ACID 事务)时,内存网格是 Redis 的有力补充。内存网格将数据分布在集群各节点的内存中,支持在数据所在节点直接执行计算逻辑,避免数据移动。

对比维度 Redis 内存网格(Hazelcast / Ignite)
数据模型 Key-Value + 数据结构 Key-Value + SQL + 计算
读写延迟 微秒级(单机) 微秒~毫秒级(分布式)
计算能力 弱(Lua 脚本有限) 强(分布式计算、SQL、机器学习推理)
事务支持 有限(MULTI/EXEC) 完整 ACID(Ignite)
持久化 RDB / AOF 原生持久化 + 第三方集成
典型特征场景 实时计数、滑动窗口、缓存 实时特征关联查询、特征聚合、在线特征工程
运维复杂度 低(成熟社区) 中(需管理 JVM 堆外内存)
选型建议: 如果实时特征以简单的 Key-Value 读写、计数、窗口聚合为主,Redis 足够胜任。如果特征计算需要跨多个 Key 做关联查询、分组聚合、甚至运行轻量级模型推理,内存网格能减少数据搬运,降低端到端延迟。

4.3 实时特征计算流程

实时特征计算流程从数据源接入到特征写入存储,通常包含以下 6 个核心步骤。每个步骤都可能成为延迟瓶颈,需要针对性优化。

  1. 数据源接入: 实时事件通过 Kafka / Pulsar / RocketMQ 等消息队列接入。建议对事件进行 schema 校验与字段标准化,避免脏数据进入计算管道。
  2. 事件解析与过滤: 流处理引擎(Flink / Kafka Streams)反序列化事件,根据业务规则过滤无效事件(如爬虫流量、测试流量)。
  3. 特征计算: 执行窗口聚合(滑动窗口、滚动窗口)、状态计算(用户累计行为、物品实时热度)、特征变换(归一化、离散化、交叉特征)。
  4. 特征存储写入: 将计算结果写入 Redis / 内存网格。写入时注意设置 TTL、使用批量写入减少网络开销。
  5. 特征版本管理: 为每个特征打上时间戳或版本号,便于在线服务识别特征时效性,也便于离线回放时对齐时间窗口。
  6. 监控与告警: 监控特征计算延迟、写入成功率、存储容量。当延迟超过阈值(如 > 5 秒)或写入失败率升高时,触发告警。
实时特征计算流程 1. 数据源接入 2. 事件解析过滤 3. 特征计算 4. 特征存储写入 5. 版本管理 6. 监控与告警:延迟、成功率、存储容量 各步骤延迟优化要点: • 数据源接入:使用 Kafka 分区并行消费,避免单分区瓶颈 • 事件解析过滤:使用 Protobuf / Avro 序列化,减少反序列化开销 • 特征计算:优先使用 Flink 状态后端(RocksDB)管理大状态,避免 OOM • 特征存储写入:使用 Redis Pipeline / 批量写入,单次写入合并多个特征
实时特征计算常见陷阱:
  • 状态膨胀: 滑动窗口或用户行为累计特征会随着时间推移占用大量内存。建议设置状态 TTL 或使用 RocksDB 状态后端将冷数据 spill 到磁盘。
  • 数据乱序: 实时事件可能因网络延迟或重试而乱序到达。使用 Flink 的 Event Time + Watermark 机制处理乱序,避免窗口聚合结果偏差。
  • 特征存储热点: 高活跃用户或热门物品的 Key 可能成为 Redis 热点,导致单节点 CPU 飙高。建议对热点 Key 做本地缓存(如 Guava Cache)或使用 Redis 读写分离架构。
  • 特征一致性问题: 实时特征与离线特征可能因计算逻辑差异产生不一致。建议在实时特征管道中保留原始事件日志,便于离线回放时对齐。

4.4 实时特征系统架构总览

综合以上内容,一个完整的实时特征系统架构通常包含数据接入层、流处理层、特征存储层、在线服务层与监控层。下图展示各层之间的协作关系:

数据接入层 Kafka Pulsar RocketMQ Schema Registry 流处理层 Flink Kafka Streams 状态后端 (RocksDB) 特征存储层 Redis Cluster Hazelcast Apache Ignite 在线服务层 推荐服务 风控服务 搜索服务 监控层:Prometheus + Grafana / ELK / 自定义告警 ← 上一章 📖 返回目录 下一章 →