特征存储架构:在线存储与离线存储、特征计算与存储分离、特征血缘与元数据管理

聊特征存储,绕不开架构。说白了,特征存储不是扔一个 Redis 或者 MySQL 就完事了。我见过太多团队,一开始图省事,把特征全塞进一个库里,结果线上延迟爆炸,离线训练又跑不动。嗯,今天咱们就把这块掰开揉碎讲清楚。

在线存储 vs 离线存储:两个世界,一套特征

先问个问题:为什么不能只用一种存储?

在线推理要求毫秒级响应,QPS 动不动几千上万。离线训练呢?要扫全量数据,吞吐量是 MB/s 甚至 GB/s 级别。这两种负载对存储的要求完全相反——一个要低延迟、高并发;一个要高吞吐、大容量。

我个人习惯把在线存储和离线存储看作「同一套特征的两个副本」:

维度 在线存储 离线存储
典型引擎 Redis、Aerospike、DynamoDB HDFS、S3、Hive、Iceberg
延迟要求 < 10ms 秒级到分钟级
数据格式 Key-Value、Hash Parquet、ORC、Avro
更新频率 实时写入 / T+1 批量同步 小时级 / 天级批量
数据量级 TB 级(热数据) PB 级(全量历史)

关键原则:在线存储只存「当前有效」的特征值,离线存储保留「所有历史快照」。两者通过统一的特征名称和版本号关联。

我在项目中遇到过一个问题:在线特征用 Redis 存,离线特征用 Hive 表存,两边特征名不一致,导致线上和离线 AUC 差 5 个点。排查了两天才发现是命名规范没对齐。所以后来我强制要求:特征名必须全局唯一,在线和离线用同一个 schema 定义。

特征计算与存储分离:解耦才是王道

早期特征工程怎么做?计算和存储绑在一起。比如在 Spark 作业里算完特征直接写 Hive,线上服务自己算自己存。这有什么问题?

  • 计算逻辑重复:离线写一套 PySpark,在线写一套 Java,两边逻辑对不上
  • 存储紧耦合:换存储引擎要改计算代码
  • 扩缩容困难:计算和存储争资源

我建议的做法是:特征计算层和特征存储层通过「特征视图」解耦。计算层只负责产出特征值,不关心数据最终落到哪里。存储层只负责读写,不关心特征怎么算出来的。

举个例子,我们用 Flink 做实时特征计算,输出到 Kafka。然后一个统一的「特征同步服务」从 Kafka 消费,分别写入 Redis(在线)和 HDFS(离线)。这样如果某天要把 Redis 换成 Aerospike,只需要改同步服务的下游,计算逻辑完全不动。

我的经验:计算与存储分离后,最大的收益不是性能,而是「团队协作效率」。特征工程师专注写计算逻辑,平台工程师专注优化存储,各管各的,互不干扰。

这里有个坑——数据一致性。我曾经遇到过在线和离线特征差了 2 小时的情况,原因是同步服务挂了,Kafka 积压。后来加了监控和自动重试,才把延迟控制在分钟级。

特征血缘与元数据管理:没有血缘,特征就是黑盒

你想想看,一个模型上线后效果变差了,你怎么知道是哪个特征出了问题?如果没有血缘追踪,你只能靠猜。

特征血缘,说白了就是记录「特征从哪里来、经过什么变换、被哪些模型使用」。我把它分成三个层级:

  1. 数据源血缘:特征来自哪个原始表、哪个字段、哪个时间窗口
  2. 计算血缘:特征经过哪些算子(聚合、JOIN、UDF)
  3. 消费血缘:特征被哪些模型、哪些线上服务引用

元数据管理是血缘的基础。每个特征至少要有这些元数据:

元数据字段 说明 示例
feature_name 全局唯一特征名 user_click_7d_cnt
feature_type 数据类型 int64 / float32 / string
owner 负责人 zhangsan@company.com
source_table 原始数据表 ods.user_behavior_log
compute_logic 计算逻辑描述或代码路径 flink_job/user_click_agg
online_store 在线存储位置 redis://cluster-01:6379/0
offline_store 离线存储位置 hdfs://ns1/features/user_click/
version 特征版本 v2.3.1

注意:元数据一定要自动化采集,不要靠人工维护。我见过一个团队用 Excel 管理特征元数据,结果三个月后 Excel 和实际特征对不上了,整个特征体系崩塌。

血缘追踪的实现方式,我推荐两种:

  • 代码注入:在特征计算框架(如 Flink、Spark)中嵌入血缘采集逻辑,自动记录输入输出
  • 日志解析:从计算任务的日志中解析血缘关系,适合已有系统改造

下面这张图展示了特征存储的整体架构,包括在线/离线存储、计算与存储分离、以及血缘元数据流转:

特征存储架构总览 数据源层 原始日志 | 业务数据库 | 实时流(Kafka) 特征计算层(计算与存储分离) Flink 实时计算 | Spark 批量计算 | 特征视图定义 在线存储 Redis / Aerospike / DynamoDB 离线存储 HDFS / S3 / Hive / Iceberg 特征同步服务(保证一致性) 特征血缘与元数据管理 数据源血缘 → 计算血缘 → 消费血缘 | 元数据注册中心 核心原则:在线/离线存储分离、计算与存储解耦、血缘全链路可追溯

最后说一句,元数据管理不是一次性工程。我建议每周做一次血缘完整性校验,确保每个特征都能追溯到原始数据源。否则时间一长,特征库就会变成「垃圾场」——没人知道哪些特征还能用,哪些已经废弃了。