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 元数据生命周期管理
特征不是一成不变的。我见过最典型的坑是:特征下线了,但没人更新元数据,结果新来的同学还在用,上线后才发现数据源已经没了。
所以,元数据必须跟着特征走,经历三个阶段:
- active(活跃):正常使用,可被搜索和引用
- deprecated(弃用):不再推荐使用,但保留历史数据
- 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 元数据可视化:一张图看懂全貌
说了这么多,我们来画一张图,把特征元数据的核心结构串起来。
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 流程中加入元数据校验步骤。每次有新特征注册,自动跑一遍规则检查,不合格的直接驳回。这样能省掉后面大量的沟通成本。