- 引言
- Feature Store 核心概念
- Feast 架构
- Materialization 流水线
- Online Store 后端配置
- Offline Store 配置
- Training-Serving Skew 防范策略
- Feature Monitoring 与 Drift Detection
- Feature Store 方案对比
- 运维注意事项
- 故障案例与恢复流程
- 生产环境部署检查清单
- 结语
- 参考资料

引言
随着机器学习模型的生产部署越来越普遍,特征(Feature)管理已经成为 MLOps 的核心课题。训练模型时使用的特征,必须在实时服务中原样复现;多个团队要共享同一份特征,避免重复计算;特征的质量与新鲜度也需要持续监控。
Feature Store 正是为解决这些问题而出现的基础设施层,集中管理特征的定义、存储、服务与监控。其中 Feast(Feature Store)是使用最广泛的开源 Feature Store,能够在复用现有数据基础设施的同时提供灵活的特征服务。
本文将依次介绍 Feature Store 的核心概念、Feast 架构、特征定义与 Entity 设计、Materialization 流水线、Online/Offline Store 配置、Training-Serving Skew 防范策略、Feature Monitoring、与 Tecton/Hopsworks 的对比、生产环境部署模式,以及故障应对全过程。
Feature Store 核心概念
为什么需要 Feature Store
在没有 Feature Store 的情况下运行 ML 流水线,会产生以下问题。
| 问题 | 说明 | 影响 |
|---|---|---|
| Training-Serving Skew | 训练与服务中的特征计算逻辑不一致 | 模型性能下降 |
| 特征重复计算 | 各团队各自实现相同的特征 | 浪费计算资源 |
| 数据泄漏 | 未来数据被混入训练数据 | 过拟合、评估结果失真 |
| 特征难以发现 | 无法得知已经存在哪些特征 | 开发效率下降 |
| 服务延迟 | 实时计算特征会带来延迟 | 用户体验变差 |
Online Store vs Offline Store
Feature Store 基本上具备两种存储。
| 项目 | Online Store | Offline Store |
|---|---|---|
| 用途 | 实时推理 | 模型训练、批量推理 |
| 延迟 | 1~10ms | 秒~分钟级 |
| 数据范围 | 仅最新值 | 全部历史 |
| 存储示例 | Redis、DynamoDB | BigQuery、Redshift、S3 |
| 查询模式 | Key-Value 查询 | SQL/DataFrame 查询 |
| 数据量 | GB 级 | TB~PB 级 |
| 一致性 | 最终一致性 | 强一致性 |
Feature Freshness 与 Consistency
特征的新鲜度(Freshness)表示反映了多近的数据。
- 批处理特征:以 1 小时~1 天为周期更新(例如:用户最近 30 天的购买次数)
- 流式特征:以秒~分钟为周期更新(例如:最近 5 分钟内的交易金额)
- 实时特征:在请求时点计算(例如:当前会话中的点击次数)
Feast 架构
整体结构
Feast 由以下组件构成。
# feature_store.yaml - Feast 项目配置
project: fraud_detection
registry: gs://ml-feature-store/registry.db
provider: gcp
offline_store:
type: bigquery
dataset: feature_store
online_store:
type: redis
connection_string: 'redis-cluster.internal:6379'
redis_type: redis_cluster
entity_key_serialization_version: 2
| 组件 | 角色 | 说明 |
|---|---|---|
| Feature Registry | 特征元数据管理 | 存储特征定义、Entity、数据源信息 |
| Offline Store | 历史数据存储 | 从 BigQuery、Redshift、Spark 等提取训练数据 |
| Online Store | 最新特征服务 | 从 Redis、DynamoDB 等进行实时查询 |
| Feature Server | REST API 服务 | 基于 FastAPI 的低延迟特征服务端点 |
| Materialization Engine | 数据同步 | 将特征从 Offline Store 复制到 Online Store |
Feature 定义与 Entity 设计
# features/fraud_detection.py
from datetime import timedelta
from feast import Entity, FeatureView, Field, FileSource, BigQuerySource
from feast.types import Float32, Int64, String
# Entity 定义 - 特征所关联的对象
user_entity = Entity(
name="user_id",
join_keys=["user_id"],
description="Unique identifier for a user",
)
merchant_entity = Entity(
name="merchant_id",
join_keys=["merchant_id"],
description="Unique identifier for a merchant",
)
# Data Source 定义
user_transactions_source = BigQuerySource(
name="user_transactions",
table="ml_data.user_transaction_features",
timestamp_field="event_timestamp",
created_timestamp_column="created_timestamp",
)
merchant_stats_source = BigQuerySource(
name="merchant_stats",
table="ml_data.merchant_statistics",
timestamp_field="event_timestamp",
)
# Feature View 定义 - 特征分组
user_transaction_features = FeatureView(
name="user_transaction_features",
entities=[user_entity],
ttl=timedelta(days=7),
schema=[
Field(name="transaction_count_7d", dtype=Int64),
Field(name="transaction_amount_avg_7d", dtype=Float32),
Field(name="transaction_amount_max_7d", dtype=Float32),
Field(name="unique_merchants_7d", dtype=Int64),
Field(name="avg_time_between_transactions", dtype=Float32),
],
source=user_transactions_source,
online=True,
tags={
"team": "fraud-detection",
"version": "v2",
},
)
merchant_risk_features = FeatureView(
name="merchant_risk_features",
entities=[merchant_entity],
ttl=timedelta(days=30),
schema=[
Field(name="chargeback_rate_30d", dtype=Float32),
Field(name="avg_transaction_amount", dtype=Float32),
Field(name="total_transactions_30d", dtype=Int64),
Field(name="risk_score", dtype=Float32),
],
source=merchant_stats_source,
online=True,
tags={
"team": "fraud-detection",
"version": "v1",
},
)
Feature Service 定义
# features/services.py
from feast import FeatureService
fraud_detection_service = FeatureService(
name="fraud_detection_v2",
features=[
user_transaction_features,
merchant_risk_features,
],
tags={
"model": "fraud_detector_v2",
"owner": "ml-team",
},
)
Materialization 流水线
批量 Materialization
# 用 Feast CLI 应用 Feature Registry
feast apply
# 执行批量 Materialization
feast materialize 2026-03-01T00:00:00 2026-03-12T00:00:00
# 增量 Materialization(自上次执行以来)
feast materialize-incremental 2026-03-12T00:00:00
用 Airflow 实现自动化
# dags/feast_materialization.py
from datetime import datetime, timedelta
from airflow import DAG
from airflow.operators.bash import BashOperator
from airflow.operators.python import PythonOperator
default_args = {
"owner": "ml-team",
"retries": 3,
"retry_delay": timedelta(minutes=5),
}
dag = DAG(
"feast_materialization",
default_args=default_args,
description="Daily feature materialization pipeline",
schedule_interval="0 6 * * *",
start_date=datetime(2026, 1, 1),
catchup=False,
tags=["feast", "mlops"],
)
# 验证特征源数据
validate_sources = PythonOperator(
task_id="validate_sources",
python_callable=lambda: __import__("feast").FeatureStore(
repo_path="/opt/feast/feature_repo"
),
dag=dag,
)
# 执行 Materialization
materialize = BashOperator(
task_id="materialize_features",
bash_command="""
cd /opt/feast/feature_repo && \
feast materialize-incremental $(date -u +%Y-%m-%dT%H:%M:%S)
""",
dag=dag,
)
# 验证 Online Store 一致性
validate_online = PythonOperator(
task_id="validate_online_store",
python_callable=lambda: print("Validating online store consistency..."),
dag=dag,
)
validate_sources >> materialize >> validate_online
Online Store 后端配置
基于 Redis 的 Online Store
# feature_store.yaml - Redis 配置
project: fraud_detection
registry: gs://ml-feature-store/registry.db
provider: gcp
online_store:
type: redis
connection_string: 'redis-cluster.internal:6379,redis-cluster.internal:6380,redis-cluster.internal:6381'
redis_type: redis_cluster
key_ttl_seconds: 604800 # 7 天
基于 DynamoDB 的 Online Store
# feature_store.yaml - DynamoDB 配置
project: fraud_detection
registry: s3://ml-feature-store/registry.db
provider: aws
online_store:
type: dynamodb
region: ap-northeast-2
table_name_template: 'feast_online_{project}_{table}'
Online Store 后端对比
| 项目 | Redis | DynamoDB | PostgreSQL |
|---|---|---|---|
| 延迟 | 0.5~2ms | 1~5ms | 2~10ms |
| 扩展性 | 手动(集群) | 自动 | 手动 |
| 成本模型 | 按实例计费 | 按请求计费 | 按实例计费 |
| TTL 支持 | 原生支持 | 原生支持 | 需手动实现 |
| 运维负担 | 中等 | 较低 | 中等 |
| 适用场景 | 需要超低延迟 | Serverless、AWS | 小规模、成本敏感 |
Offline Store 配置
基于 BigQuery 的 Offline Store
# feature_store.yaml - BigQuery 配置
project: fraud_detection
registry: gs://ml-feature-store/registry.db
provider: gcp
offline_store:
type: bigquery
dataset: feature_store
location: asia-northeast3
生成训练数据(Point-in-Time Join)
# training_data.py
from feast import FeatureStore
import pandas as pd
store = FeatureStore(repo_path="./feature_repo")
# Entity DataFrame - 用于训练的时点与 Entity
entity_df = pd.DataFrame({
"user_id": ["user_001", "user_002", "user_003", "user_001"],
"merchant_id": ["merch_100", "merch_200", "merch_100", "merch_300"],
"event_timestamp": pd.to_datetime([
"2026-03-01 10:00:00",
"2026-03-02 14:30:00",
"2026-03-03 09:15:00",
"2026-03-05 16:45:00",
]),
"label": [0, 1, 0, 1], # 是否为欺诈
})
# 提取具有 Point-in-Time 正确性的特征
training_df = store.get_historical_features(
entity_df=entity_df,
features=[
"user_transaction_features:transaction_count_7d",
"user_transaction_features:transaction_amount_avg_7d",
"user_transaction_features:transaction_amount_max_7d",
"user_transaction_features:unique_merchants_7d",
"merchant_risk_features:chargeback_rate_30d",
"merchant_risk_features:risk_score",
],
).to_df()
print(training_df.head())
print(f"Training data shape: {training_df.shape}")
Online 特征查询(实时推理)
# inference.py
from feast import FeatureStore
store = FeatureStore(repo_path="./feature_repo")
# 实时特征查询
feature_vector = store.get_online_features(
features=[
"user_transaction_features:transaction_count_7d",
"user_transaction_features:transaction_amount_avg_7d",
"user_transaction_features:unique_merchants_7d",
"merchant_risk_features:chargeback_rate_30d",
"merchant_risk_features:risk_score",
],
entity_rows=[
{"user_id": "user_001", "merchant_id": "merch_100"},
],
).to_dict()
print(feature_vector)
# 输出示例:
# {
# "user_id": ["user_001"],
# "merchant_id": ["merch_100"],
# "transaction_count_7d": [23],
# "transaction_amount_avg_7d": [45000.5],
# "unique_merchants_7d": [8],
# "chargeback_rate_30d": [0.02],
# "risk_score": [0.15]
# }
Training-Serving Skew 防范策略
Training-Serving Skew 是导致 ML 模型性能下降的最常见原因之一。
Skew 产生原因与应对
| 原因 | 说明 | 应对方式 |
|---|---|---|
| 特征计算逻辑不一致 | 训练/服务使用了不同的代码 | 通过 Feature Store 统一为单一来源 |
| 数据泄漏 | 未来数据被混入训练数据 | 应用 Point-in-Time Join |
| 特征新鲜度差异 | 批处理 vs 实时更新周期不一致 | 管理 TTL 并监控 Freshness |
| Schema 变更 | 特征定义发生了变化 | 对 Feature Registry 进行版本管理 |
| NULL 处理差异 | 默认值处理方式不一致 | 设定统一的默认值策略 |
借助 Feast 防范 Skew
# skew_detection.py
import pandas as pd
import numpy as np
from feast import FeatureStore
from scipy import stats
store = FeatureStore(repo_path="./feature_repo")
def detect_training_serving_skew(
feature_name: str,
training_values: pd.Series,
sample_size: int = 1000,
):
"""比较训练数据与 Online Store 的特征分布。"""
# 从 Online Store 采样
online_features = []
entity_rows = [{"user_id": f"user_{i:04d}"} for i in range(sample_size)]
online_result = store.get_online_features(
features=[feature_name],
entity_rows=entity_rows,
).to_df()
serving_values = online_result[feature_name.split(":")[-1]].dropna()
# 用 KS 检验比较分布
ks_stat, p_value = stats.ks_2samp(
training_values.dropna(),
serving_values,
)
# 计算 PSI(Population Stability Index)
psi = calculate_psi(training_values.dropna(), serving_values)
return {
"feature": feature_name,
"ks_statistic": ks_stat,
"p_value": p_value,
"psi": psi,
"skew_detected": psi > 0.2 or p_value < 0.05,
}
def calculate_psi(expected, actual, bins=10):
"""计算 PSI(Population Stability Index)。"""
breakpoints = np.linspace(
min(expected.min(), actual.min()),
max(expected.max(), actual.max()),
bins + 1,
)
expected_counts = np.histogram(expected, breakpoints)[0] / len(expected)
actual_counts = np.histogram(actual, breakpoints)[0] / len(actual)
# 防止除零
expected_counts = np.clip(expected_counts, 0.001, None)
actual_counts = np.clip(actual_counts, 0.001, None)
psi = np.sum(
(actual_counts - expected_counts) * np.log(actual_counts / expected_counts)
)
return psi
Feature Monitoring 与 Drift Detection
监控指标
# monitoring/feature_monitor.py
from dataclasses import dataclass
from datetime import datetime
from typing import Optional
import pandas as pd
@dataclass
class FeatureStats:
feature_name: str
timestamp: datetime
mean: float
std: float
min_val: float
max_val: float
null_rate: float
unique_count: int
p99_latency_ms: Optional[float] = None
def compute_feature_stats(df: pd.DataFrame, feature_name: str) -> FeatureStats:
"""计算特征的统计信息。"""
series = df[feature_name]
return FeatureStats(
feature_name=feature_name,
timestamp=datetime.utcnow(),
mean=series.mean(),
std=series.std(),
min_val=series.min(),
max_val=series.max(),
null_rate=series.isnull().sum() / len(series),
unique_count=series.nunique(),
)
def check_drift_alerts(
current: FeatureStats,
baseline: FeatureStats,
thresholds: dict,
) -> list:
"""检查特征漂移告警。"""
alerts = []
# 检查均值变化率
if baseline.mean != 0:
mean_change = abs(current.mean - baseline.mean) / abs(baseline.mean)
if mean_change > thresholds.get("mean_change", 0.3):
alerts.append(
f"Mean drift detected: {baseline.mean:.4f} -> {current.mean:.4f} "
f"(change: {mean_change:.2%})"
)
# NULL 比例变化
null_diff = abs(current.null_rate - baseline.null_rate)
if null_diff > thresholds.get("null_rate_change", 0.05):
alerts.append(
f"Null rate change: {baseline.null_rate:.4f} -> {current.null_rate:.4f}"
)
# 范围异常
if current.max_val > baseline.max_val * thresholds.get("max_multiplier", 2.0):
alerts.append(
f"Max value anomaly: {current.max_val} (baseline max: {baseline.max_val})"
)
return alerts
Feature Store 方案对比
| 项目 | Feast | Tecton | Hopsworks |
|---|---|---|---|
| 许可证 | Apache 2.0(开源) | 商业(托管) | AGPL + 商业 |
| 架构 | 模块化、可插拔 | 托管、端到端 | 集成平台 |
| 实时特征 | 有限 | 原生支持 | 支持 |
| 流式处理 | 基于 Push | Kafka/Kinesis 原生支持 | 集成 Kafka |
| 特征转换 | Python SDK | Spark/Pandas/SQL | Spark/Flink |
| 监控 | 基础水平 | 内置(自动告警) | 内置(漂移检测) |
| 治理 | 基础水平 | RBAC、审计日志 | RBAC、审计、血缘追踪 |
| 云 | 多云 | AWS/Databricks | AWS/Azure/GCP |
| 适用组织 | 注重灵活性、具备工程能力的团队 | 大型企业、需要实时 ML | 受监管行业、追求一体化 |
| 社区 | 非常活跃(与 CNCF 相关) | 商业支持 | 活跃 |
运维注意事项
1. Entity 设计原则
- 按业务领域来设计 Entity 键(user_id、order_id、device_id 等)
- 复合 Entity 键会影响查询性能,应谨慎使用
- Entity 键的基数过高时,Online Store 的内存占用会急剧上升
2. TTL 管理
- Online Store 的 TTL 应比 Materialization 周期设置得更宽裕
- TTL 过短时,Materialization 延迟会导致返回 NULL 值
- TTL 过长时,Online Store 的存储成本会上升
3. Schema 变更管理
- 新增特征是向后兼容的,但删除特征或更改类型则需要重新训练模型
- 为便于 Feature View 的版本管理,应在名称中包含版本号(例如:
user_features_v2) - Schema 变更时,务必验证与现有模型的兼容性
4. Materialization 失败应对
# 检查 Materialization 状态
feast materialize-incremental 2026-03-12T00:00:00 --verbose
# 仅重新执行特定 Feature View
feast materialize-incremental 2026-03-12T00:00:00 \
--feature-views user_transaction_features
# 检查 Online Store 的特征新鲜度
feast feature-views list
故障案例与恢复流程
故障案例 1:Online Store 故障(Redis 集群宕机)
症状:所有实时推理请求的特征查询均失败
恢复流程:
- 检查并恢复 Redis 集群状态
- 若无法恢复,则切换到备用 Redis(Sentinel/Cluster Failover)
- 重新执行 Materialization,恢复 Online Store 数据
- 验证特征新鲜度后再恢复推理服务
故障案例 2:Materialization 流水线失败
症状:Online Store 中的特征数据未更新,导致服务的是过期数值
# 特征新鲜度检查脚本
from feast import FeatureStore
from datetime import datetime, timedelta
store = FeatureStore(repo_path="./feature_repo")
# 检查各 Feature View 最近一次 Materialization 的时间
for fv in store.list_feature_views():
if fv.materialization_intervals:
last_mat = fv.materialization_intervals[-1]
staleness = datetime.utcnow() - last_mat.end_date
if staleness > timedelta(hours=24):
print(f"ALERT: {fv.name} is stale by {staleness}")
else:
print(f"OK: {fv.name} last materialized at {last_mat.end_date}")
else:
print(f"WARNING: {fv.name} has never been materialized")
恢复流程:
- 从 Materialization 日志中查明失败原因(Offline Store 访问问题、Schema 变更等)
- 确认数据源的可用性
- 对失败的 Feature View 重试 Materialization
- 验证 Online Store 的特征一致性
故障案例 3:检测到 Feature Drift
症状:模型性能指标逐渐下降
恢复流程:
- 在 Feature Monitoring 仪表盘中确认漂移情况
- 查明原因(数据流水线变更、上游 Schema 变更、实际分布变化)
- 如有需要,修改特征流水线
- 若漂移严重,触发模型重新训练
生产环境部署检查清单
在生产环境部署 Feature Store 时,务必确认以下项目。
| 项目 | 检查点 | 建议设置 |
|---|---|---|
| Online Store 可用性 | 集群配置、副本数 | Redis Cluster 3+ 节点 |
| Materialization 周期 | 业务需求所要求的新鲜度 | 按 SLA 设置周期 |
| TTL 设置 | Materialization 失败时的影响 | Materialization 周期的 2~3 倍 |
| 备份策略 | Online/Offline Store 备份 | 每日快照 |
| 监控 | 特征漂移、延迟 | Prometheus + Grafana |
| 告警 | Materialization 失败、漂移 | 集成 PagerDuty/Slack |
| 安全 | 认证/授权、网络 | IAM、VPC、TLS |
结语
Feature Store 是解决生产环境 ML 模型运维中特征管理复杂性的核心基础设施。Feast 凭借开源的灵活性与模块化架构,提供了能够自然融入现有基础设施的方案。
核心要点归纳如下。
- Online/Offline Store 分离:分别针对实时服务与批量训练的需求进行优化
- Point-in-Time Correctness:通过 Feature Store 的时间旅行查询,从根本上防止数据泄漏
- Training-Serving Skew 防范:从单一特征定义同时支持训练与服务,保证一致性
- Materialization 自动化:与 Airflow 等工具集成,稳定运行特征更新流水线
- Feature Monitoring:通过漂移检测与特征质量监控,持续维持模型性能
引入 Feature Store 不是针对单个模型的投资,而是提升整个组织 ML 成熟度的投资。建议从小规模起步,在一个生产模型上验证之后,再逐步扩大范围。
参考资料
- Feast Official Documentation
- Feast Architecture Overview
- Feature Store Architecture and Storage - DragonflyDB
- A Comparative Analysis: Feast vs Tecton vs Hopsworks - Uplatz
- Feature Store 101: Build, Serve, and Scale ML Features - Aerospike
- What is a Feature Store? - Databricks
- Solving Training-Serving Skew with Feast - Medium
현재 단락 (1/411)
随着机器学习模型的生产部署越来越普遍,特征(Feature)管理已经成为 MLOps 的核心课题。训练模型时使用的特征,必须在实时服务中原样复现;多个团队要共享同一份特征,避免重复计算;特征的质量与新...