- 引言
- 1. Feature Store 的必要性
- 2. Feature Store 架构
- 3. Feast 框架深度解析
- 4. Feast 安装与项目配置
- 5. Feature View 与 Entity 定义
- 6. 在线/离线服务实现
- 7. ML 流水线集成
- 8. Feature Store 对比分析
- 9. 故障案例与应对策略
- 10. 运维检查清单
- 结语

引言
ML 模型的生产环境性能,比起模型架构,更大程度上取决于特征的质量与一致性。当数据科学家在 Jupyter Notebook 中编写的特征转换逻辑,与实际服务服务器中实现的逻辑出现细微差异的那一刻,训练-服务偏差(Training-Serving Skew)便会发生,模型性能随之急剧下滑。
Feature Store 正是在架构层面解决这一问题的核心基础设施。本文将以开源 Feature Store Feast 为中心,从架构设计到在线/离线服务实现、借助 Airflow 与 Kubeflow 完成 ML 流水线集成,再到与 Tecton、Hopsworks 等竞品平台的对比分析,进行全面梳理。目标不止于一份简单的安装指南,而是希望成为在生产环境中运维数百万实体与数十个 Feature View 的团队可以参考的实战指南。
1. Feature Store 的必要性
缺乏特征管理时出现的问题
在没有 Feature Store 的情况下运维 ML 系统,会反复出现以下问题。
- 特征逻辑重复:训练流水线中计算
avg_order_amount_30d的代码,与服务服务器中的代码不一致。NULL 处理方式、聚合窗口范围、时区等方面会产生细微差异。 - 特征发现性缺失:A 团队已经计算过的
user_click_rate_7d特征,B 团队并不知情,于是重新计算了一遍。组织内的特征资产因此被孤岛化。 - 无法进行时间回溯:无法回答"3 周前这个用户的特征值是多少"这样的问题,导致再训练与调试都变得不可行。
- 服务延迟:如果在模型推理时从多个数据源实时计算特征,p99 延迟会飙升至数百毫秒。
Feature Store 解决的核心课题
Feature Store 承担着特征的单一真相来源(Single Source of Truth)这一角色。由于离线训练数据与在线服务数据都由同一份特征定义生成,逻辑不一致的问题被从根源上消除。借助中央注册表,特征可以被检索与复用,而 Point-in-Time Join 则能够精确复现某个历史时点的特征值。
2. Feature Store 架构
Feature Store 的核心架构由离线存储、在线存储、注册表三个组件构成。
离线存储(Offline Store)
存储大量历史特征数据,并在生成训练数据时执行 Point-in-Time Join。以 BigQuery、Snowflake、Redshift、S3/Parquet 等大规模分析型数据仓库或数据湖作为后端。需要能够高效扫描数十 TB 规模的数据。
在线存储(Online Store)
面向实时服务的低延迟键值存储。以 Redis、DynamoDB、Bigtable 等作为后端,需要以实体键为基准,在 p99 10ms 以内返回最新的特征值。离线存储中的数据通过 Materialization 过程同步到在线存储。
注册表(Registry)
存储 Entity、Feature View、Feature Service 等元数据的中央目录。可以基于文件(本地、S3、GCS)或基于 SQL(PostgreSQL、MySQL)来运行。生产环境中推荐使用基于 SQL 的注册表。
3. Feast 框架深度解析
Feast(Feature Store)是 2019 年由 Gojek 与 Google 发起的开源项目,目前由 Linux Foundation 管理。Feast 的核心优势在于可插拔架构——可以在保留现有基础设施(Spark、Kafka、Redis、Snowflake 等)的同时,只叠加一层 Feature Store。
核心概念
- Entity:特征所关联的业务对象(例如用户、商品、司机)
- Feature View:源自同一数据源的特征分组的逻辑单元
- Feature Service:特定模型所使用的一组特征的集合
- Data Source:特征数据的来源(BigQuery、Parquet、Kafka 等)
- Materialization:将数据从离线存储同步到在线存储的过程
4. Feast 安装与项目配置
安装与初始化
# 安装 Feast(含 Redis、PostgreSQL 支持)
pip install 'feast[redis,postgres]'
# 初始化项目
feast init feature_repo
cd feature_repo
# 目录结构
# feature_repo/
# feature_store.yaml -- 项目配置
# definitions.py -- Entity、Feature View 定义
# data/ -- 示例数据
项目配置(feature_store.yaml)
project: my_ml_platform
provider: gcp
registry:
registry_type: sql
path: postgresql://feast:feast@db-host:5432/feast_registry
cache_ttl_seconds: 60
online_store:
type: redis
connection_string: redis-host:6379,password=secret
offline_store:
type: bigquery
dataset: feast_offline
entity_key_serialization_version: 2
这份配置中有三点值得关注。第一,使用基于 SQL 的注册表,支持多个团队的并发访问。第二,在线存储使用 Redis,保证毫秒级响应。第三,离线存储使用 BigQuery,处理大规模历史数据的 Join。
5. Feature View 与 Entity 定义
定义 Entity 与 Feature View
from datetime import timedelta
from feast import Entity, FeatureView, Field, FileSource, BigQuerySource
from feast.types import Float32, Float64, Int64, String
# Entity 定义
customer = Entity(
name="customer_id",
description="顾客的唯一标识符",
)
driver = Entity(
name="driver_id",
description="司机的唯一标识符",
)
# BigQuery 数据源定义
customer_stats_source = BigQuerySource(
name="customer_stats_source",
table="my_project.feast_dataset.customer_stats",
timestamp_field="event_timestamp",
created_timestamp_column="created_timestamp",
)
# Feature View 定义
customer_stats_fv = FeatureView(
name="customer_stats",
entities=[customer],
ttl=timedelta(days=3),
schema=[
Field(name="total_orders", dtype=Int64),
Field(name="avg_order_amount", dtype=Float64),
Field(name="lifetime_value", dtype=Float64),
Field(name="preferred_category", dtype=String),
Field(name="churn_risk_score", dtype=Float32),
],
source=customer_stats_source,
online=True,
tags={
"team": "growth",
"version": "v2",
},
)
driver_stats_source = BigQuerySource(
name="driver_stats_source",
table="my_project.feast_dataset.driver_stats",
timestamp_field="event_timestamp",
)
driver_stats_fv = FeatureView(
name="driver_stats",
entities=[driver],
ttl=timedelta(hours=6),
schema=[
Field(name="avg_rating", dtype=Float64),
Field(name="total_trips", dtype=Int64),
Field(name="acceptance_rate", dtype=Float64),
Field(name="avg_delivery_time_min", dtype=Float32),
],
source=driver_stats_source,
online=True,
)
定义 Feature Service
将特定模型所使用的特征打包成 Feature Service 来管理。
from feast import FeatureService
# 用于流失预测模型的 Feature Service
churn_prediction_svc = FeatureService(
name="churn_prediction_service",
features=[
customer_stats_fv[["total_orders", "avg_order_amount", "lifetime_value", "churn_risk_score"]],
],
tags={
"model": "churn_prediction_v3",
"owner": "growth-team",
},
)
# 用于司机匹配模型的 Feature Service
driver_matching_svc = FeatureService(
name="driver_matching_service",
features=[
driver_stats_fv[["avg_rating", "acceptance_rate", "avg_delivery_time_min"]],
customer_stats_fv[["preferred_category"]],
],
)
6. 在线/离线服务实现
离线服务(生成训练数据)
离线服务通过 Point-in-Time Join 精确查询过去某个时点的特征值,这是防止 Feature Leakage 的核心机制。
from feast import FeatureStore
import pandas as pd
store = FeatureStore(repo_path="feature_repo/")
# 训练用实体 DataFrame(含标签)
entity_df = pd.DataFrame({
"customer_id": [1001, 1002, 1003, 1001, 1002],
"event_timestamp": pd.to_datetime([
"2026-01-15 10:00:00",
"2026-01-15 11:00:00",
"2026-01-16 09:00:00",
"2026-02-01 10:00:00",
"2026-02-01 11:00:00",
]),
"churned": [0, 1, 0, 1, 0], # 标签
})
# 通过 Point-in-Time Join 生成训练数据
training_df = store.get_historical_features(
entity_df=entity_df,
features=[
"customer_stats:total_orders",
"customer_stats:avg_order_amount",
"customer_stats:lifetime_value",
"customer_stats:churn_risk_score",
],
).to_df()
print(training_df.head())
# customer_id | event_timestamp | churned | total_orders | avg_order_amount | ...
# 1001 | 2026-01-15 10:00:00 | 0 | 42 | 35.50 | ...
在线服务(实时推理)
在线服务以毫秒级延迟返回最新的特征值。
# 在线特征查询
online_features = store.get_online_features(
features=[
"customer_stats:total_orders",
"customer_stats:avg_order_amount",
"customer_stats:churn_risk_score",
],
entity_rows=[
{"customer_id": 1001},
{"customer_id": 1002},
],
).to_dict()
print(online_features)
# 输出示例:
# {
# "customer_id": [1001, 1002],
# "total_orders": [45, 12],
# "avg_order_amount": [35.50, 28.00],
# "churn_risk_score": [0.15, 0.82],
# }
Materialization(从离线同步到在线)
# 全量 Feature View Materialization
feast materialize 2026-01-01T00:00:00 2026-03-10T00:00:00
# 增量 Materialization(仅同步上次运行之后的变化)
feast materialize-incremental 2026-03-10T00:00:00
7. ML 流水线集成
用 Airflow 构建特征流水线
将 Feast 的 Materialization 以 Airflow DAG 的形式自动化,可以实现稳定的运维。
from airflow import DAG
from airflow.decorators import task
from datetime import datetime, timedelta
default_args = {
"owner": "ml-platform",
"retries": 3,
"retry_delay": timedelta(minutes=5),
}
with DAG(
dag_id="feast_materialization_pipeline",
default_args=default_args,
schedule_interval="0 */4 * * *", # 每 4 小时运行一次
start_date=datetime(2026, 1, 1),
catchup=False,
) as dag:
@task()
def validate_source_data():
"""验证源数据质量"""
from great_expectations import get_context
context = get_context()
result = context.run_checkpoint(checkpoint_name="feature_source_check")
if not result.success:
raise ValueError("源数据质量验证失败")
return True
@task()
def materialize_features():
"""从离线存储向在线存储执行 Materialization"""
from feast import RepoConfig, FeatureStore
from feast.infra.online_stores.redis import RedisOnlineStoreConfig
from feast.repo_config import RegistryConfig
repo_config = RepoConfig(
project="my_ml_platform",
provider="gcp",
registry=RegistryConfig(
registry_type="sql",
path="postgresql://feast:feast@db-host:5432/feast_registry",
),
online_store=RedisOnlineStoreConfig(
connection_string="redis-host:6379",
),
)
store = FeatureStore(config=repo_config)
store.materialize_incremental(end_date=datetime.utcnow())
return True
@task()
def validate_online_store():
"""验证在线存储的特征值"""
from feast import FeatureStore
store = FeatureStore(repo_path="feature_repo/")
# 使用示例实体查询在线特征
result = store.get_online_features(
features=["customer_stats:total_orders"],
entity_rows=[{"customer_id": 1001}],
).to_dict()
if result["total_orders"][0] is None:
raise ValueError("特征未加载到在线存储")
return True
@task()
def notify_completion():
"""发送 Slack 通知"""
import requests
requests.post(
"https://hooks.slack.com/services/YOUR/WEBHOOK/URL",
json={"text": "Feast Materialization 已完成"},
)
validate_source_data() >> materialize_features() >> validate_online_store() >> notify_completion()
Kubeflow Pipelines 集成
在 Kubeflow Pipelines 中,可以按组件单元定义 Feast 相关任务。
from kfp import dsl
from kfp.dsl import component, Output, Dataset
@component(
base_image="python:3.10",
packages_to_install=["feast[redis,postgres]>=0.40.0"],
)
def feast_materialize_op(
project_name: str,
registry_path: str,
redis_connection: str,
):
from feast import RepoConfig, FeatureStore
from feast.infra.online_stores.redis import RedisOnlineStoreConfig
from feast.repo_config import RegistryConfig
from datetime import datetime
config = RepoConfig(
project=project_name,
provider="gcp",
registry=RegistryConfig(
registry_type="sql",
path=registry_path,
),
online_store=RedisOnlineStoreConfig(
connection_string=redis_connection,
),
)
store = FeatureStore(config=config)
store.materialize_incremental(end_date=datetime.utcnow())
@component(
base_image="python:3.10",
packages_to_install=["feast[redis,postgres]>=0.40.0", "scikit-learn"],
)
def train_model_op(
project_name: str,
model_output: Output[Dataset],
):
from feast import FeatureStore
import pandas as pd
from sklearn.ensemble import GradientBoostingClassifier
import pickle
store = FeatureStore(repo_path="feature_repo/")
entity_df = pd.read_parquet("gs://my-bucket/training_entities.parquet")
training_df = store.get_historical_features(
entity_df=entity_df,
features=[
"customer_stats:total_orders",
"customer_stats:avg_order_amount",
"customer_stats:churn_risk_score",
],
).to_df()
X = training_df.drop(columns=["customer_id", "event_timestamp", "churned"])
y = training_df["churned"]
model = GradientBoostingClassifier(n_estimators=200)
model.fit(X, y)
with open(model_output.path, "wb") as f:
pickle.dump(model, f)
@dsl.pipeline(name="feast-ml-training-pipeline")
def feast_training_pipeline():
materialize_task = feast_materialize_op(
project_name="my_ml_platform",
registry_path="postgresql://feast:feast@db-host:5432/feast_registry",
redis_connection="redis-host:6379",
)
train_task = train_model_op(
project_name="my_ml_platform",
)
train_task.after(materialize_task)
8. Feature Store 对比分析
| 项目 | Feast | Tecton | Hopsworks | SageMaker Feature Store |
|---|---|---|---|---|
| 许可证 | Apache 2.0(开源) | 商业(托管) | AGPL / 商业 | 绑定 AWS |
| 部署方式 | 自托管 | SaaS / VPC | SaaS / 自托管 | AWS 托管 |
| 在线存储 | Redis、DynamoDB、PostgreSQL | DynamoDB(内置) | RonDB(内置,高性能) | 自有存储 |
| 离线存储 | BigQuery、Snowflake、Redshift、Spark | Spark、Snowflake | Apache Hudi | S3 + Glue Catalog |
| 流式支持 | Kafka Push(基础) | Kafka、Kinesis(原生) | Kafka、Spark Streaming | Kinesis |
| 转换引擎 | On-Demand Transform | Spark、SQL、Python DSL | Spark、Flink | SageMaker Processing |
| Point-in-Time Join | 支持 | 支持(高阶) | 支持 | 有限支持 |
| 注册表 | SQL、基于文件 | 内置(Web UI) | 内置(Hopsworks UI) | AWS Glue |
| GenAI/向量支持 | 有限 | 支持 Embedding | Embedding + RAG | 无 |
| 成本 | 免费(仅基础设施成本) | 高(企业级) | 中等 | 按 AWS 用量计费 |
| 适用对象 | 重视灵活性、偏好开源的团队 | 企业级、实时需求强 | 受监管行业、需要治理能力 | 已深度使用 AWS 生态的用户 |
核心差异总结
- Feast:灵活性最高。可以按现有基础设施自由选择各个组件,但运维负担需要团队自行承担。
- Tecton:交钥匙方案。流式特征流水线能力强大,但成本较高,适合实时 ML 为核心业务的组织。
- Hopsworks:数据治理与审计日志能力强,因而受到金融、医疗等受监管行业的青睐。基于 RonDB 的在线存储,其延迟水平约为 SageMaker 的 15%。
- SageMaker Feature Store:对已经深度绑定 AWS 生态的组织来说很方便,但存在供应商锁定风险。
9. 故障案例与应对策略
防止 Training-Serving Skew
即便引入了 Feature Store,训练-服务偏差也不会完全消失。以下是典型的发生场景与应对方式。
场景 1:TTL 超期导致的过期(stale)特征
如果在线存储的 TTL 为 6 小时,而 Materialization 批处理因故障停止运行长达 12 小时,部分特征便会以 null 返回。应对方法是在 Materialization 失败时立即发送告警,并将 TTL 设置为 Materialization 周期的至少 3 倍。
场景 2:特征定义变更导致的兼容性破坏
如果把 avg_order_amount 特征的聚合窗口从 30 天改为 90 天,已训练模型的兼容性就会被破坏。应对方法是不要改动已有特征,而是新增一个特征(例如 avg_order_amount_90d)。
场景 3:时区不一致
如果离线训练数据以 UTC 为基准,而在线源数据使用本地时区,特征值就会出现差异。所有时间戳都必须统一为 UTC。
延迟优化
在线服务延迟升高的原因与解决方法:
- 原因:单次请求中查询了过多的 Feature View。
- 解决:通过 Feature Service 只打包所需的特征,并利用批量查询(在一次
get_online_features调用中传入多个实体)。 - 原因:Redis 集群出现热键(hotkey)问题。
- 解决:在实体键中使用哈希标签,使键均匀分布。
保证数据一致性
离线存储与在线存储之间的数据一致性依赖于 Materialization。为强化这一点,可以:
- 在 Materialization 之后执行采样验证任务,比对离线与在线的特征值。
- 建立特征漂移监控,检测特征分布的异常变化。
- 在源数据流水线中集成 Great Expectations 等数据质量工具。
10. 运维检查清单
用于稳定运维生产环境 Feature Store 的检查清单:
设计阶段
- 实体设计:是否定义了符合业务领域的实体键
- TTL 设置:各 Feature View 的 TTL 是否与数据更新周期相匹配
- 离线/在线分离:并非所有 Feature View 都需要进入在线存储。是否已识别出可以设置为
online=False的特征 - 注册表:是否使用基于 SQL 的注册表,以防止并发访问冲突
流水线配置
- Materialization 调度:是否通过 Airflow 或 Cron 配置了周期性 Materialization
- 故障告警:Materialization 失败时是否配置了 Slack/PagerDuty 告警
- 源数据验证:是否使用 Great Expectations 等工具预先验证源数据质量
- 增量 Materialization:是否使用
materialize-incremental以避免全量重处理
监控
- 在线存储延迟:是否监控 p50、p95、p99 延迟
- 特征新鲜度:是否追踪各 Feature View 最近一次 Materialization 的时间
- 特征漂移:是否有监控用于检测特征分布的变化
- null 比例:是否追踪在线特征查询返回 null 的比例
安全与治理
- RBAC:Feature View 的访问权限是否按团队分离
- 审计日志:特征定义的变更历史是否被记录
- PII 脱敏:包含个人信息的特征是否应用了适当的脱敏处理
结语
Feature Store 是将 ML 系统成熟度提升一个台阶的核心基础设施。凭借开源的灵活性与可插拔架构,Feast 对大多数组织而言都是一个不错的起点。但引入 Feature Store 本身不应成为目的,重点必须放在明确的业务价值上——保证特征逻辑的一致性、防止训练-服务偏差、提升特征的可复用性。
把 Airflow 或 Kubeflow 的流水线集成、基于 Redis 的在线服务,以及通过 SQL 注册表进行的元数据管理组合起来,就可以构建出一个能够稳定运维数十个 Feature View 与数百万实体的生产级 Feature Store。根据组织的规模与需求,Tecton 或 Hopsworks 这类托管方案也值得纳入考虑。
현재 단락 (1/328)
ML 模型的生产环境性能,比起模型架构,更大程度上取决于**特征的质量与一致性**。当数据科学家在 Jupyter Notebook 中编写的特征转换逻辑,与实际服务服务器中实现的逻辑出现细微差异的那...