3. 特征元数据管理:特征名称、类型、来源、描述、创建时间、负责人等元数据设计

聊到特征注册,很多人第一反应就是「把特征名记下来」。嗯,这没错,但远远不够。我见过不少团队,一开始只记了个名字,三个月后回来一看——这个特征是谁建的?数据从哪来的?能不能用?全成了谜。

说白了,元数据就是特征的「身份证」。没有它,特征就是个黑盒。今天我们就来拆解一下,一套靠谱的特征元数据到底该长什么样。

3.1 元数据到底管什么?

我个人习惯把特征元数据分成三大类:

  • 标识类:特征叫什么、属于哪个特征组、版本号是多少
  • 溯源类:数据从哪来、谁建的、什么时候建的、有没有依赖
  • 技术类:数据类型、存储位置、计算逻辑、是否可空

你想想看,如果这三个维度都记录清楚了,后续的排查、复用、下线,是不是就清晰多了?

核心原则:元数据不是为了填表而填表,而是为了回答「这个特征能不能用、怎么用、出问题了找谁」这三个问题。

3.2 核心字段设计

下面这张表是我在多个项目中沉淀下来的「最小必要字段集」。少了它,特征管理会出问题;多了,大家又懒得填。

字段 类型 说明 是否必填
feature_name string 全局唯一特征标识,建议用 snake_case 是
feature_type enum int64 / float64 / string / bool / list / map 是
source string 数据来源,如 hive_table.column / kafka_topic 是
description text 特征含义、取值范围、业务背景 推荐
owner string 负责人邮箱或团队别名 是
created_at timestamp 首次注册时间 自动
updated_at timestamp 最后修改时间 自动
version int 当前版本号,从 1 开始递增 自动
status enum active / deprecated / archived 是
tags list[string] 用于分类和搜索,如 "风控"、"实时" 可选

小技巧:feature_type 建议用枚举而不是自由文本。我曾经见过有人填 "int"、"integer"、"INT64" 三种写法,后面做类型校验时简直想哭。

3.3 元数据存储选型

存元数据,我推荐用 关系型数据库 + 缓存 的组合。为什么?

  • MySQL / PostgreSQL:存全量元数据,支持事务和复杂查询
  • Redis:缓存高频访问的元数据,比如特征类型和版本号

举个例子,一个简单的元数据表结构:

CREATE TABLE feature_metadata (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    feature_name VARCHAR(255) NOT NULL UNIQUE,
    feature_type ENUM('int64','float64','string','bool','list','map') NOT NULL,
    source VARCHAR(512) NOT NULL,
    description TEXT,
    owner VARCHAR(128) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    version INT DEFAULT 1,
    status ENUM('active','deprecated','archived') DEFAULT 'active',
    tags JSON,
    INDEX idx_owner (owner),
    INDEX idx_status (status)
);

这里我特意加了 tags 字段用 JSON 类型,方便后期做灵活的分类和搜索。嗯,MySQL 8.0 以上对 JSON 的支持已经很成熟了,放心用。

3.4 元数据生命周期管理

特征不是一成不变的。我见过最典型的坑是:特征下线了,但没人更新元数据,结果新来的同学还在用,上线后才发现数据源已经没了。

所以,元数据必须跟着特征走,经历三个阶段:

  1. active(活跃):正常使用,可被搜索和引用
  2. deprecated(弃用):不再推荐使用,但保留历史数据
  3. archived(归档):彻底下线,仅用于审计和回溯

避坑指南:我曾经在某个项目中,直接删除了废弃特征的元数据。结果三个月后,一个离线模型报错,查了半天才发现是引用了已删除的特征。从那以后,我再也不敢直接 delete 了——软删除才是正道。

3.5 元数据血缘与依赖

一个特征可能依赖另一个特征,比如「用户月均消费金额」依赖于「用户单笔消费金额」。这种依赖关系如果不记录,改了一个特征,下游全崩了都不知道。

我建议在元数据中增加 dependencies 字段,记录上游特征列表:

{
  "feature_name": "user_avg_monthly_spend",
  "dependencies": ["user_total_spend", "user_active_months"],
  "derived_formula": "user_total_spend / user_active_months"
}

这样,当上游特征发生变化时,系统可以自动通知下游负责人。说白了,这就是特征层面的「影响分析」。

3.6 元数据可视化:一张图看懂全貌

说了这么多,我们来画一张图,把特征元数据的核心结构串起来。

特征元数据核心结构 特征元数据 标识类 feature_name, version, group 溯源类 source, owner, created_at 技术类 feature_type, status, tags 生命周期:active → deprecated → archived 血缘依赖:dependencies, derived_formula 元数据是特征治理的基石,缺了它,特征就是黑盒 标识 + 溯源 + 技术 + 生命周期 + 血缘 = 完整元数据

3.7 元数据 API 设计

有了元数据,还得有接口让别人用。我一般会暴露这样几个核心 API:

  • register_feature(metadata):注册新特征
  • get_feature(feature_name, version):获取指定版本的元数据
  • list_features(filter):按状态、类型、负责人等条件搜索
  • update_feature(feature_name, metadata):更新元数据,自动递增版本号
  • deprecate_feature(feature_name):标记为弃用

举个例子,注册特征的请求体大概长这样:

POST /api/v1/features/register
{
  "feature_name": "user_avg_monthly_spend",
  "feature_type": "float64",
  "source": "dwd.user_behavior_di.avg_spend",
  "description": "用户近30天平均月消费金额,单位元",
  "owner": "ml-team@company.com",
  "tags": ["风控", "实时"],
  "dependencies": ["user_total_spend", "user_active_months"]
}

返回的元数据会自动带上 version: 1、created_at 和 status: active。

3.8 元数据质量:别让垃圾数据进来

最后,我想强调一点:元数据也是数据,也需要质量保障。我见过最离谱的情况是,有人把 feature_type 填成了 "hello world"。

所以,注册接口一定要做校验:

  • feature_name 不能重复,且符合命名规范
  • feature_type 必须在枚举范围内
  • source 格式必须合法(比如表名.字段名)
  • owner 必须是有效的邮箱或团队名

我的习惯:在 CI/CD 流程中加入元数据校验步骤。每次有新特征注册,自动跑一遍规则检查,不合格的直接驳回。这样能省掉后面大量的沟通成本。