3. 特征存储与元数据管理

聊到特征复用,有个问题绕不开——特征到底存哪儿?怎么管?

我见过不少团队,特征写了一大堆,但问起来都说不清楚哪个特征在哪个表里、是谁生成的、版本对不对。说白了,这就是缺了特征存储和元数据管理。

这一章,我们就来拆解特征存储的架构设计、元数据注册中心怎么搭,以及特征 Schema 怎么管。

3.1 特征存储架构

特征存储不是简单的数据库。它要同时满足在线推理的低延迟和离线训练的高吞吐。我习惯把它分成三层来看。

层级 存储介质 典型场景 延迟要求
在线存储 Redis / HBase / Cassandra 实时特征查询 < 10ms
近线存储 Kafka + Flink 状态后端 窗口聚合特征 < 1s
离线存储 HDFS / S3 + Parquet 批量特征计算 分钟级

这三层不是孤立的。离线存储会定期同步到在线存储,近线存储则作为两者之间的缓冲。我在项目中遇到过一个问题:在线特征更新太频繁,导致 Redis 内存暴涨。后来加了 TTL 和冷热分离,才算稳住。

核心原则:在线存储只放高频访问的特征,低频特征走离线查询。别把所有鸡蛋放一个篮子里。

3.2 元数据注册中心

元数据注册中心,说白了就是特征的「户口本」。每个特征从出生到废弃,所有信息都得记下来。

我建议至少包含以下字段:

  • 特征名称:全局唯一,比如 user_purchase_30d_sum
  • 特征来源:哪个表、哪个字段、哪个 pipeline 生成的
  • 特征类型:连续型、离散型、文本型、向量型
  • 特征版本:语义版本号,比如 1.2.0
  • 创建时间:首次注册的时间戳
  • 责任人:谁负责维护这个特征
  • 标签:用于搜索和分类,比如 用户画像、风控

嗯,这里要注意。元数据注册中心不能只存静态信息。它还得记录特征的依赖关系。比如特征 A 依赖特征 B 和特征 C,那 B 或 C 变更时,A 就得重新计算。

我的经验:用图数据库(比如 Neo4j)来存依赖关系,查询起来比关系型数据库快一个数量级。我曾经用 MySQL 存依赖,结果一个特征变更,要跑五分钟才知道影响范围。换成图数据库后,秒级返回。

3.3 特征 Schema 管理

特征 Schema 管理,是元数据管理中最容易出问题的环节。为什么?因为特征 Schema 变了,下游模型可能直接崩掉。

我总结了一套 Schema 管理的「三不原则」:

  1. 不删字段:老模型可能还在用,删了直接报错
  2. 不改类型:int 变 float,模型加载时类型不匹配,训练直接挂
  3. 不改语义:比如 user_age 从「注册年龄」改成「当前年龄」,模型理解就错了

那怎么管理变更?我建议用 Protobuf 或 Avro 来定义特征 Schema。举个例子:

syntax = "proto3";

message UserFeature {
  string user_id = 1;
  int32 age = 2;
  float purchase_30d_sum = 3;
  repeated string tags = 4;
  // 新增字段必须加 optional
  optional float purchase_7d_sum = 5;
}

你看,新增字段用 optional,老模型不感知,新模型直接用。这就是向后兼容。

避坑指南:我曾经在项目里直接改了字段类型,从 int32 改成 int64,结果线上模型加载时抛异常,回滚花了两个小时。从那以后,我强制所有 Schema 变更必须走评审流程,并且要有兼容性检查。

3.4 架构总览

说了这么多,我们来看一张整体架构图。它把特征存储、元数据注册中心和 Schema 管理串在了一起。

特征存储与元数据管理架构 数据源 特征计算 特征存储 模型训练 在线推理 元数据注册中心 Schema 管理 在线存储 (Redis) 近线存储 (Kafka) 离线存储 (HDFS) 说明: 1. 元数据注册中心记录特征的来源、版本、依赖关系 2. Schema 管理确保特征变更向后兼容 3. 存储分层满足不同场景的延迟和吞吐需求

这张图把整个流程串起来了。数据源经过特征计算,写入特征存储。元数据注册中心记录整个过程,Schema 管理保证变更安全。模型训练和在线推理从特征存储里取数据。

你想想看,如果没有元数据注册中心,一个特征改了,你都不知道哪些模型受影响。如果没有 Schema 管理,线上模型随时可能崩。这两块,是特征复用的基石。

总结一下:特征存储解决「存哪儿」的问题,元数据注册中心解决「是什么」的问题,Schema 管理解决「怎么变」的问题。三者缺一不可。