字节跳动推荐系统特征存储设计:从万亿级数据到毫秒级响应的架构实践
引言:特征存储的技术挑战与设计目标
在推荐系统领域,特征存储(Feature Store)作为连接数据工程与机器学习的核心枢纽,面临着双重挑战:既要处理日均万亿级的特征更新,又要满足在线服务毫秒级的查询延迟。字节跳动的Monolith平台通过创新的分布式架构和高效的存储引擎,构建了一套支持高吞吐写入与低延迟读取的特征存储系统。本文将深入剖析其设计原理,包括数据模型、存储架构、分布式策略与性能优化,为构建大规模推荐系统特征存储提供实践指南。
核心设计指标
| 指标 | 目标值 | 技术挑战 |
|---|---|---|
| 特征规模 | 日均新增10亿+特征 | 如何高效存储稀疏特征 |
| 查询延迟 | P99 < 5ms | 数据本地化与缓存策略 |
| 更新频率 | 秒级实时更新 | 读写分离与一致性保障 |
| 容错能力 | 99.99%可用性 | 分布式部署与故障转移 |
| 存储成本 | 压缩率>10:1 | 高效编码与存储优化 |
特征数据模型设计:灵活应对多样化特征需求
Monolith平台采用Protocol Buffers定义统一的特征数据模型,支持离散特征、连续特征和序列特征等多种类型,同时通过可扩展的结构设计满足不同业务场景需求。
核心数据结构定义
// idl/matrix/proto/feature.proto
syntax = "proto2";
package idl.matrix.proto;
// 离散序列特征
message Fixed64List {
repeated fixed64 value = 1 [packed = true];
}
// 浮点型连续值序列特征
message FloatList {
repeated float value = 1 [packed = true];
}
message Feature {
optional string name = 1; // 特征名称,以fc_开头
// 基础特征类型(一次只能使用一种)
repeated fixed64 fid = 2 [packed = true]; // 离散id特征
repeated float float_value = 3 [packed = true]; // 连续浮点特征
repeated int64 int64_value = 4 [packed = true]; // 连续整数特征
repeated bytes bytes_value = 5; // 原始字节特征
// 序列特征类型
repeated Fixed64List fid_list = 6; // 离散id序列
repeated FloatList float_list = 7; // 浮点序列
repeated Int64List int64_list = 8; // 整数序列
repeated BytesList bytes_list = 9; // 字节序列
}
特征类型适用场景
| 特征类型 | 存储格式 | 典型应用 | 空间效率 |
|---|---|---|---|
| fid | packed fixed64 | 用户ID、物品ID | 高(每个ID占8字节) |
| float_value | packed float | 点击率、时长 | 中(每个值占4字节) |
| fid_list | 嵌套Fixed64List | 用户行为序列 | 极高(共享存储结构) |
| bytes_value | 原始字节 | 文本、图像特征 | 低(无压缩) |
核心架构设计:分层存储与计算分离
Monolith特征存储采用分层架构,将计算与存储分离,通过多级缓存和分片策略实现高可用与高性能。
整体架构流程图
flowchart TD
subgraph 数据写入层
A[离线特征ETL] -->|批量导入| B[分布式文件系统]
C[实时特征流] -->|Kafka| D[流处理服务]
end
subgraph 存储层
B --> E[特征存储主集群]
D --> E
E --> F{哈希分片}
F --> G[PS节点1]
F --> H[PS节点2]
F --> I[PS节点N]
end
subgraph 服务层
J[特征查询API] --> K[本地缓存]
K --> L[一致性哈希路由]
L --> M[远程PS查询]
M --> G
M --> H
M --> I
end
subgraph 监控与运维
N[指标收集] --> O[Prometheus]
P[日志分析] --> Q[ELK]
end
核心组件解析
- 特征存储主集群:基于分布式哈希表实现,支持PB级数据存储
- PS节点:负责分片数据的存储与计算,每个节点管理部分哈希空间
- 一致性哈希路由:确保特征查询能够高效定位到目标节点
- 多级缓存:包括本地内存缓存、Redis集群缓存和PS节点缓存
分布式特征存储实现:从单节点到大规模集群
Monolith通过创新的分布式哈希表设计和分片策略,解决了大规模特征存储的扩展性问题。
分布式哈希表设计
# monolith/native_training/distributed_ps.py
class DistributedHashTable(hash_table_ops.BaseHashTable):
def __init__(self, ps_num, config, hash_table_factory):
self._ps_num = ps_num
self._hash_tables = []
learning_rate_tensor = config.call_learning_rate_fns()
for i in range(self._ps_num):
with ps_device(i): # 绑定到特定PS节点
config.set_learning_rate_tensor(learning_rate_tensor)
self._hash_tables.append(hash_table_factory(i, config))
def lookup(self, ids: tf.Tensor) -> tf.Tensor:
unique_ids, idx = tf.unique(ids)
indices = tf.math.floormod(unique_ids, self._ps_num) # 哈希分片
split_ids = distribution_ops.split_by_indices(indices, unique_ids, self._ps_num)
split_embeddings = []
for i in range(self._ps_num):
with ps_device(i):
embeddings_part = self._hash_tables[i].lookup(split_ids[i])
split_embeddings.append(embeddings_part)
# 合并查询结果
return distribution_ops.map_id_to_embedding(split_ids, split_embeddings, ids)
分片策略对比
| 分片方式 | 实现原理 | 优点 | 缺点 |
|---|---|---|---|
| 哈希取模 | id % 节点数 | 实现简单,负载均匀 | 扩容时需数据迁移 |
| 一致性哈希 | 哈希环映射 | 支持平滑扩容 | 实现复杂,小规模集群负载不均 |
| 动态分片 | 基于数据量自动调整 | 资源利用率高 | 需中央协调服务 |
Monolith采用哈希取模+预分片策略,通过固定分片数量(通常256或512)实现逻辑分片与物理节点的解耦,兼顾扩展性与简单性。
特征生命周期管理:从创建到淘汰的全流程
特征存储不仅需要高效存储特征,还需要管理特征的全生命周期,包括创建、更新、老化和淘汰。
特征配置示例
# monolith/native_training/feature.py
@dataclass
class FeatureSlotConfig:
name: str = None
has_bias: bool = False
bias_initializer: entry.Initializer = entry.ZerosInitializer()
default_vec_initializer: entry.Initializer = entry.RandomUniformInitializer()
default_vec_optimizer: entry.Optimizer = entry.AdagradOptimizer(initial_accumulator_value=1.0)
default_vec_compressor: entry.Compressor = entry.Fp16Compressor()
hashtable_config: entry.HashTableConfig = entry.CuckooHashTableConfig()
occurrence_threshold: int = 0 # 出现次数阈值
expire_time: int = 36500 # 特征过期时间(天)
特征淘汰机制
Monolith实现了两种特征淘汰策略:
- 基于时间的淘汰:通过
expire_time配置特征的存活周期,定期清理过期特征 - 基于频率的淘汰:通过
occurrence_threshold设置最低出现次数,过滤低频特征
# monolith/native_training/hash_table_ops.py
def save(self, basename: tf.Tensor) -> "HashTable":
return self._copy_with_new_table(
hash_table_ops.monolith_hash_table_save(
self._table,
basename,
slot_expire_time_config=self._slot_expire_time_config,
nshards=self._saver_parallel
)
)
高性能查询优化:从毫秒到微秒的响应提速
为满足推荐系统低延迟需求,Monolith从多个层面进行查询优化:
多级缓存架构
stateDiagram-v2
[*] --> 请求
请求 --> 本地缓存: 检查LRU缓存
本地缓存 --> 命中: 返回结果
本地缓存 --> 远程查询: 未命中
远程查询 --> Redis集群: 检查分布式缓存
Redis集群 --> 命中2: 返回结果并更新本地缓存
Redis集群 --> PS节点查询: 未命中
PS节点查询 --> 返回结果: 更新Redis和本地缓存
返回结果 --> [*]
特征压缩与编码优化
Monolith支持多种特征压缩策略,显著降低网络传输和存储开销:
| 压缩算法 | 压缩率 | 性能开销 | 适用场景 |
|---|---|---|---|
| FP16 | 2:1 | 低 | 连续特征向量 |
| 变长编码 | 3-5:1 | 中 | 整数ID序列 |
| LZ4 | 2-4:1 | 中高 | 字符串特征 |
| 稀疏表示 | 10-100:1 | 低 | 高维稀疏特征 |
批处理与预取优化
通过请求批处理和预取技术,Monolith将随机查询转为批量操作,大幅提升吞吐量:
# monolith/native_training/distribution_ops.py
def fused_reduce_sum_and_split(value_rowids, embeddings, batch_size_tensor, slice_dims):
# 融合reduce和split操作,减少内存访问
return hash_table_ops.monolith_fused_reduce_sum_and_split(
value_rowids, embeddings, batch_size_tensor, slice_dims
)
持久化与故障恢复:数据可靠性保障
特征数据的可靠性至关重要,Monolith通过多重机制确保数据不丢失:
检查点机制
# monolith/native_training/serving.md
# 模型检查点结构示例
hdfs:///user/model_checkpoint/exported_models/
├── entry/ # 入口服务模型
├── ps_0/ # PS节点0的检查点
├── ps_1/ # PS节点1的检查点
└── ps_2/ # PS节点2的检查点
Monolith采用异步增量检查点机制,平衡性能与数据一致性:
- 全量检查点:每日一次,保存完整数据
- 增量检查点:每小时一次,仅保存变更数据
- 实时WAL:记录写操作日志,确保崩溃恢复
主从复制与故障转移
每个PS节点维护多个副本,通过半同步复制确保数据可靠性:
- 写操作需至少同步到一个从副本才返回成功
- 主节点故障时,自动提升从节点为新主
- 采用Raft协议实现副本间的一致性
实践指南:特征存储的最佳使用方式
特征定义最佳实践
-
特征命名规范
- 使用
fc_前缀标识特征列,如fc_user_id - 包含特征类型和业务含义,如
fc_item_category_seq
- 使用
-
特征选择建议
- 离散特征优先使用
fid类型,节省存储空间 - 高基数特征(>1000万)建议使用哈希映射
- 序列特征长度控制在200以内,避免过度计算
- 离散特征优先使用
性能调优参数
| 参数 | 推荐值 | 说明 |
|---|---|---|
| 哈希表类型 | CuckooHash | 平衡查找速度与内存效率 |
| 压缩算法 | FP16 | 对嵌入向量压缩率高,精度损失小 |
| 本地缓存大小 | 2GB | 根据内存情况调整,建议不超过总内存的20% |
| 批处理大小 | 1024 | 权衡延迟与吞吐量 |
常见问题排查
-
查询延迟高
- 检查是否命中缓存
- 分析热点特征分布
- 调整分片策略,避免热点集中
-
内存占用过大
- 启用更激进的压缩算法
- 降低本地缓存大小
- 调整特征过期策略
-
数据不一致
- 检查检查点恢复流程
- 验证主从复制状态
- 确认网络稳定性
总结与展望
Monolith特征存储通过创新的分布式架构、高效的数据结构和精细化的优化策略,成功支撑了字节跳动大规模推荐系统的特征管理需求。未来,特征存储将向以下方向发展:
- 智能特征工程:结合AI自动生成和选择特征
- 实时特征计算:降低特征从产生到可用的延迟
- 多模态特征支持:统一管理文本、图像等多模态特征
- 自适应存储策略:根据特征特性自动选择最优存储方式
通过不断演进,特征存储将不仅是数据的容器,更成为机器学习系统的"特征大脑",为推荐系统提供更强大的动力。
附录:核心API参考
特征定义API
# 创建特征槽
config = FeatureSlotConfig(
name="user_behavior",
has_bias=True,
default_vec_initializer=RandomUniformInitializer(minval=-0.01, maxval=0.01),
default_vec_optimizer=AdagradOptimizer(initial_accumulator_value=0.1),
expire_time=30 # 30天过期
)
slot = feature_factory.create_feature_slot(config)
# 创建特征列
fc = FeatureColumn(
feature_slot=slot,
feature_name="user_click_seq",
combiner=FeatureColumn.reduce_sum()
)
特征查询API
# 批量查询特征
def lookup_features(feature_names, ids):
embeddings = {}
for name in feature_names:
embeddings[name] = feature_store.lookup(name, ids[name])
return embeddings
特征更新API
# 实时更新特征
def update_features(feature_name, ids, values):
return feature_store.update(
feature_name,
ids=tf.convert_to_tensor(ids, dtype=tf.int64),
values=tf.convert_to_tensor(values, dtype=tf.float32)
)
文档版本:v1.0
最后更新:2025-09-07
适用场景:推荐系统、机器学习平台、大规模特征管理
收藏与分享:如果本文对您有帮助,请点赞收藏,关注获取更多推荐系统技术实践分享!
下期预告:《万亿参数模型的分布式训练实践》即将发布,敬请期待!
Kimi-K2.5Kimi K2.5 是一款开源的原生多模态智能体模型,它在 Kimi-K2-Base 的基础上,通过对约 15 万亿混合视觉和文本 tokens 进行持续预训练构建而成。该模型将视觉与语言理解、高级智能体能力、即时模式与思考模式,以及对话式与智能体范式无缝融合。Python00- QQwen3-Coder-Next2026年2月4日,正式发布的Qwen3-Coder-Next,一款专为编码智能体和本地开发场景设计的开源语言模型。Python00
xw-cli实现国产算力大模型零门槛部署,一键跑通 Qwen、GLM-4.7、Minimax-2.1、DeepSeek-OCR 等模型Go06
PaddleOCR-VL-1.5PaddleOCR-VL-1.5 是 PaddleOCR-VL 的新一代进阶模型,在 OmniDocBench v1.5 上实现了 94.5% 的全新 state-of-the-art 准确率。 为了严格评估模型在真实物理畸变下的鲁棒性——包括扫描伪影、倾斜、扭曲、屏幕拍摄和光照变化——我们提出了 Real5-OmniDocBench 基准测试集。实验结果表明,该增强模型在新构建的基准测试集上达到了 SOTA 性能。此外,我们通过整合印章识别和文本检测识别(text spotting)任务扩展了模型的能力,同时保持 0.9B 的超紧凑 VLM 规模,具备高效率特性。Python00
KuiklyUI基于KMP技术的高性能、全平台开发框架,具备统一代码库、极致易用性和动态灵活性。 Provide a high-performance, full-platform development framework with unified codebase, ultimate ease of use, and dynamic flexibility. 注意:本仓库为Github仓库镜像,PR或Issue请移步至Github发起,感谢支持!Kotlin08
VLOOKVLOOK™ 是优雅好用的 Typora/Markdown 主题包和增强插件。 VLOOK™ is an elegant and practical THEME PACKAGE × ENHANCEMENT PLUGIN for Typora/Markdown.Less00