特征服务设计总结
整理自
spider_scf_featurerouting+neirong_scf_appmainrecom代码梳理,及大厂 Feature Store / 在线特征实践阅读笔记。
更新:2026-05-19
目录
- 特征参数设计:两种方案对比
- 本司架构:featurerouting 特征路由服务
- appmainrecom 侧特征加载
- 数据源要求与前缀约定
- 跨城兴趣分(WList)接入分析
- 大厂常见架构模式
- 推荐阅读文章
1. 特征参数设计:两种方案对比
场景:司机近 7 天有效行程记录,如何定义「参数」与「特征」?
| 对比维度 | 方案 1:参数=司机,特征=近7天有效行程 | 方案 2:参数=司机+7天+有效,特征=行程记录 |
|---|---|---|
| 设计定位 | 标准特征服务。高并发、低延迟 KV 读取 | 在线数据服务 / 轻量计算引擎。带过滤、聚合逻辑 |
| 读性能 | 极高(<5ms)。Redis/HBase 预计算值一次读出 | 较低且不稳定。服务内过滤聚合,随数据量劣化 |
| 高并发 QPS | 极高。推荐/派单核心链路可支撑百万级 QPS | 较低。动态参数导致缓存命中率低 |
| 模型对接 | 友好。Schema 固定、特征列表确定 | 较差。特征长度/口径动态变化 |
| 维护成本 | 低。离线/实时算好写入,在线服务当「哑管道」 | 高。特征服务内堆业务逻辑 |
大厂常用思路:在线侧尽量 方案 1——参数是实体 ID(user/item),时间窗口、过滤条件在 离线/近线生产 时固化进特征名或存储 key;动态维度(如「当前出发城」)要么在 调用方拼 key,要么走 LocalLoad / 算子 而非塞进特征服务 RPC 参数。
2. 本司架构:featurerouting 特征路由服务
2.1 项目定位
Maven 双模块 SCF 服务:按 featureId 路由到不同存储/画像服务,批量拉取特征字符串。
spider_scf_featurerouting/
├── contract/ # IFeatureRoutingService、FeatureResult、GroupBatchRequest
└── service/ # Router、FeatureSource、字典、底层 IO 工具
2.2 两类「加载」
| 类型 | 时机 | 数据源 | 作用 |
|---|---|---|---|
| 元数据加载 | 启动 + 每 5 分钟 | MySQL | featureId → sourceId、查询字段、压缩方式 |
| 特征值加载 | 每次 RPC | WTable / WList / Redis / 画像 SCF | 按 id 批量取特征值 |
元数据表:
t_feature_datasource:source_id,source_type,source_config(JSON)t_feature_config:feature_id,source_id,feature_config(JSON)
2.3 RPC 入口与调用链
| 方法 | 说明 |
|---|---|
getFeature |
单 id,内部转 getFeatureBatch |
getFeatureBatch |
Router:按数据源分组并行读 |
getFeatureBatchGroup |
GroupParallelRouter:多组并行,组内同 Router |
SCF Client
→ FeatureRoutingService
→ Router.route(featureIds) # featureId → sourceId(FeatureConfigDict)
→ 并行 FeatureReader × N
→ FeatureSourceFactory.createFeatureSource(sourceId)
→ IFeatureSource.getFeaturesBatch(ids, featureIds)
→ 合并 ConcurrentHashMap<id, Map<featureId, value>>
超时:Router 45s,GroupParallelRouter 46s。
2.4 四种 FeatureSource(source_type)
| type_id | 枚举 | 实现类 | 底层 IO |
|---|---|---|---|
| 1 | WLIST | WListFeatureSource | WlistClient.scan |
| 2 | WTABLE | WTableFeatureSource | WtableClient.mGet |
| 3 | REDIS_CLUSTER | RedisClusterFeatureSource | Redisson readAllMapAsync |
| 4 | FACETAGADSVC | FaceTagADSvcFeatureSource | 画像 SCF / OneService |
统一接口:IFeatureSource.getFeaturesBatch(ids, featureIds) → 返回与 ids 等长的 List<Map<featureId, value>>。
2.5 各源读法要点
Redis
id= Hash 的 完整 key(原样,不自动加前缀)- 一次
readAllMap拉整表,再按feature_config.field取子字段 - 唯一启用 Guava 本地缓存的数据源(
isUseCache=true)
WTable
rowKey = id,colKey = feature_config.field- 支持 Gzip / Snappy 解压(
compressId) - 注入 cache 但 未使用
WList
id= scan 的 list key- 单 feature 只输出 一个字段 的多行值,用
|拼接 - 配置:
field,filter,isAsc,limit,fieldType
画像
id当作 imei;personasType:1=FaceTagADSvc,2=RealtimeFaceSvc,≥100=OneService- personasType=1 时服务内部会拼
wimei前缀
2.6 部署配置(featurerouting)
路径:service/deploy/featurerouting/config/
| 文件 | 用途 |
|---|---|
config.properties |
MySQL(featuredb_*) |
wtable_{clientId}.properties |
WTable 集群 |
wlist_{clientId}.properties |
WList 集群 |
{clientId}.json |
Redisson Redis 集群 |
注意:无 prefix 配置项;featurerouting 不会对 id 做任何前缀/后缀处理。
3. appmainrecom 侧特征加载
3.1 与 featurerouting 的关系
- 依赖:
featurerouting.contract - 封装:
FeatureRoutingService.getFeatureBatchGroup→IFeatureRoutingService.PROXY
3.2 用户特征两条路径(并行)
| 路径 | register_type | 加载类 | 说明 |
|---|---|---|---|
| TRIBE_FEATURE | 0 | BaseUserFeatureLoad |
调 featurerouting RPC |
| LOCAL_FILL | 本地填充 | LocalUserFeatureLoad |
map_func → LocalLoadFactory → 各 *Load 实现 |
物品特征:ItemFeatureLoad,key_type=9(物品 nid),同样走 featurerouting 分组批量。
3.3 配置表
| 表 | 项目 | 作用 |
|---|---|---|
t_feature_register |
appmainrecom | feature_id, key_type, key_prefix, key_suffix, map_func, register_config |
t_feature_config |
featurerouting | 查询语义(field、filter、compress 等) |
t_feature_datasource |
featurerouting | 连接与 source_type |
feature_id 两边必须一致。
3.4 Key 拼装规则(调用方)
用户特征:
物品特征:
取回后按 group 元数据 剥掉 prefix/suffix 还原 nid。
特殊:suffix=vector 时用桶号作 Redis key,非 nid。
4. 数据源要求与前缀约定
4.1 能否有前缀?
| 问题 | 结论 |
|---|---|
| Redis/WList/WTable 能有前缀吗? | 可以,前缀是 key 的一部分 |
| featurerouting 会自动加前缀吗? | 不会 |
| 前缀在哪配? | appmainrecom t_feature_register.key_prefix / key_suffix |
| 存储侧要求? | 写入 key 必须等于 调用时传入的完整 id |
| 画像能用 prefix 吗? | 一般 不要;type=1 内部已有 wimei 逻辑 |
4.2 新特征接入检查清单
- 确定存储类型(Redis Hash / WTable / WList / 画像)
- 确定 完整 key(含前缀)在存储中的实际形态
- featurerouting:注册
t_feature_datasource+t_feature_config,部署对应 properties/json - appmainrecom:注册
t_feature_register,key_prefix/suffix与存储一致 - 选
map_func将 RPC 返回的 string 转为目标FeatureItem类型 - 用 拼好的完整 id 调
getFeatureBatch验证
5. 跨城兴趣分(WList)接入分析
5.1 现状(已实现,走 LocalLoad)
| 项 | 值 |
|---|---|
| 类 | CrossCityFeatureFromWListLoad |
| 注册 | register_type=LOCAL_FILL, map_func=CrossCityFeatureFromWListLoad |
| WList | index=10 → wlist10.properties, bid=1378287663 |
| 兴趣分 | tableId=1, key=imei + "_" + departCityId |
| 强度 | tableId=2, key=imei |
| register_id | 600100(map_list), 600101(double) |
逻辑:多字段 scan → 去重 → 排除出发城 → 按分数排序 → 组装 List<Map<String, FeatureItem>>。
下游:RecomPrepare / CrossCityExpParam.fillSortedCityScoreListFromInterestFeature。
5.2 若走统一 featurerouting 的卡点
| 卡点 | 说明 | 严重度 |
|---|---|---|
| 动态 key | 需 imei_出发城,BaseUserFeatureLoad 仅支持静态 prefix/suffix |
★★★ |
| 返回结构 | featurerouting WList 只返回单字段 \| 拼接 string,非 map_list |
★★★ |
| 业务逻辑 | 去重、过滤、排序在 WList 源中不存在 | ★★ |
| 双表双 key | 需两个 source_id(tableId 在数据源级) | ★ |
| 部署分裂 | featurerouting 需新增 wlist_1378287663.properties,当前无 wlist10 |
★ |
| WList 缓存 | FeatureSource 注入 cache 但未使用 | ★ |
5.3 推荐路线
| 方案 | 说明 |
|---|---|
| A. 维持 LocalLoad | 零改造,已上线;与 featurerouting 并行共存 |
| B. 半统一 | LocalLoad 内调 featurerouting,本地仍拼 key + 解析 map_list |
| C. 真统一 | 改 BaseUserFeatureLoad 支持动态后缀 + 扩展 WListFeatureSource 多字段/JSON + 新 FeatureMap |
6. 大厂常见架构模式
flowchart TB
subgraph offline [离线/近线]
A[日志/行为] --> B[Spark/Flink 特征生产]
B --> C[Hive/离线表]
C --> D[同步 KV/Redis/WTable/WList]
end
subgraph online [在线推理]
E[推荐服务] --> F[特征加载层]
F --> G{路由/注册表}
G --> H[KV 批量读]
G --> I[画像 RPC]
G --> J[本地算子 LocalLoad]
F --> K[特征拼接 / 算子图]
K --> L[模型 / 规则]
end
D --> H
| 模式 | 大厂做法 | 本司对应 |
|---|---|---|
| 三层分离 | 离线生产 / 近线同步 / 在线只读 | featurerouting = 在线读+路由 |
| Feature Store | 特征注册、元数据、按 ID 批量拉取 | MySQL + t_feature_register |
| 多存储路由 | 按 featureId 路由 | FeatureSourceType 四类 |
| Key 约定 | 调用方或平台拼复合 key | key_prefix/suffix;跨城 LocalLoad |
| 特征算子 | FeatureOperator、配置化 DAG | LocalLoad + map_func |
| 训练/在线一致 | 同一 feature 定义 | feature_id 对齐 |
7. 推荐阅读文章
7.1 国内大厂(优先)
网页版(MkDocs)不会把裸
https://...自动变成可点链接,须写成[标题](url)。Obsidian 里裸 URL 能点,属编辑器行为差异。
| 文章 | 重点 |
|---|---|
| 美团外卖排序特征生产框架(2016) | KV 推送 + FeatureOperator 算子 |
| 美团在线特征数据存取(2017) | 低延迟、压缩、分层存储 |
| 美团在线特征生产调度(2017) | 近线调度与同步 |
| 美团外卖特征平台(2021) | 语义合并、多任务调度 |
| 腾讯新闻推荐架构升级 | 配置化特征算子、图优化 |
| 网易新闻特征算子篇 | 算子抽象、图剪枝 |
| 阿里 PAI FeatureStore 推荐实践 | 离在线一体 |
| FeatHub 流批一体特征平台 | Flink、Point-in-time |
| 蚂蚁 Flink 实时特征平台 | Skyline、高性能 Serving |
7.2 国际大厂
| 文章 | 重点 |
|---|---|
| Uber Palette Metastore | 特征元数据与 Serving |
| Uber Michelangelo 概览 | ML 平台全貌 |
| LinkedIn 开源 Feathr | 按名取特征、训练在线一致 |
| Vertex AI Feature Store | 托管 Feature Store |
| 在线 Serve 特征值 | Feature View 在线读 |
| Netflix Metaflow 与 ML 系统 | 数据层特征工程 |
7.3 开源 Feature Store
| 资源 |
|---|
| What is a Feature Store (Feast) |
| Feast 推理架构 |
| Feast 在线性能调优 |
| Tecton 在线 Serving FAQ |
7.4 建议阅读顺序
- 美团 2016 特征生产框架 → 建立「生产→KV→在线加载」直觉
- 美团 2021 特征平台 → 平台化与语义合并
- Uber Palette → 元数据 + Serving
- Feast 概念文 → 通用术语
- 腾讯新闻架构 → 在线算子工程化
附录:关键代码路径
| 模块 | 路径 |
|---|---|
| featurerouting 入口 | spider_scf_featurerouting/.../FeatureRoutingService.java |
| 路由 | .../Router.java, GroupParallelRouter.java |
| 工厂 | .../FeatureSourceFactory.java |
| WList 源 | .../WListFeatureSource.java |
| app 用户特征 RPC | neirong_scf_appmainrecom/.../BaseUserFeatureLoad.java |
| app 物品特征 RPC | .../ItemFeatureLoad.java |
| 跨城 LocalLoad | .../CrossCityFeatureFromWListLoad.java |
| 特征注册实体 | .../FeatureRegisterBean.java |
本文档为收件箱草稿,后续可归档至「推荐系统 / 特征工程」专题目录并补充具体 feature_id 配置样例。