news 2026/9/17 7:13:09

Feast SQLite Online Store:从 feature_store.yaml 配置到源码实现的本地在线特征服务

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Feast SQLite Online Store:从 feature_store.yaml 配置到源码实现的本地在线特征服务

Feast SQLite Online Store:从 feature_store.yaml 配置到源码实现的本地在线特征服务

【免费下载链接】feastThe Open Source Feature Store for AI/ML项目地址: https://gitcode.com/GitHub_Trending/fe/feast

Feast 的 SQLite Online Store 是最轻量的在线存储后端:它把特征值物化到一个磁盘上的 SQLite 数据库文件中,供 Python/Go SDK 在线读取。本文基于 SQLite Online Store 官方参考文档 展开,结合 SqliteOnlineStore 源码实现、Go 端实现 与 单元测试,完整讲解其配置项、底层数据模型、读写路径、plan/update/teardown 基础设施管理,以及向量与全文检索扩展能力,帮助你在本地开发、示例演示或轻量级部署中正确配置并理解它的行为边界。

一、定位与核心特征

按照 官方文档 的定义,SQLite Online Store 提供将特征值物化(materialize)到 SQLite 数据库以进行在线特征服务的能力,其核心特征是:

  • 所有特征值存储在磁盘上的 SQLite 数据库中——整个后端就是单个.db文件,无需额外安装数据库服务;
  • 仅持久化每个 key 的最新特征值——写入采用 upsert 语义,历史值不保留,这与文档功能矩阵中"不支持 TTL、不支持过期数据删除"是同一枚硬币的两面。

源码层面同样明确了它的定位边界。SqliteOnlineStore 类的 docstring 直接写明:"SQLite implementation of the online store interface. Not recommended for production usage."也就是说,它是 Feast 核心在线存储实现之一(与 Redis、DynamoDB、Snowflake、Datastore 并列,见 在线存储总览),但官方建议将其用于本地开发、快速上手、演示与测试场景,而非生产环境。这一判断也体现在它的功能短板上(见第七节功能矩阵):不支持同 key 并发写、不支持 TTL、Java SDK 无法读取。

二、配置:在 feature_store.yaml 中启用 sqlite online store

启用方式非常直接。在特征仓库根目录的feature_store.yaml中把online_store.type设为sqlite并指定数据库文件路径即可:

project: my_feature_repo registry: data/registry.db provider: local online_store: type: sqlite path: data/online_store.db

仓库中的 MCP Feature Store 示例配置 就是一个真实用例:type: sqlitepath: data/online_store.db,并搭配type: file的离线存储和type: mcp的 Feature Server,构成一个完全本地化的运行形态。快速入门文档 与 feature-store-yaml 参考文档 中给出的默认仓库模板也采用同样的 SQLite 在线存储。

2.1 配置项全集

文档指出完整配置项见SqliteOnlineStoreConfig。对照 源码中的 Pydantic 模型定义 及其混入的 VectorStoreConfig,配置项全集如下:

配置项类型默认值说明
typeLiteral["sqlite"]"sqlite"在线存储类型选择器,也接受完整类名feast.infra.online_stores.sqlite.SqliteOnlineStore(见_get_db_path中的断言,L232-L243)
pathStrictStr"data/online.db"SQLite 数据库文件路径;若为相对路径,会基于特征仓库路径(repo_path)解析为绝对路径
vector_enabledOptional[bool]False是否启用向量相似检索(依赖sqlite_vec扩展,见第六节)
similarityOptional[str]"cosine"KNN 检索的向量相似度度量;主要服务于"不支持在检索时动态配置度量"的向量库
text_search_enabledboolFalse是否启用基于 SQLite FTS5/BM25 的全文检索
enable_openai_compatible_storeboolFalse是否以"OpenAI 兼容存储"模式运行:写入时为每行物化value_text数值化列value_num,使检索支持基于特征数值的元数据过滤

注意一个容易踩坑的细节:path的解析逻辑在_get_db_path中——只有当配置了repo_pathpath是相对路径时,才会拼接成repo_path / path,否则原样使用。这意味着feast materializefeast apply等操作在不同工作目录下执行时,实际读写的.db文件可能位于不同位置,排障时应以特征仓库根目录为基准确认数据库文件位置。

三、底层数据模型:每个 Feature View 一张表

SQLite Online Store 的数据模型是按 Feature View 共置(collocated by feature view)——这与功能矩阵中 "collocated by feature view: yes" 一致。每个启用在线服务的 Feature View 对应一张物理表,表名由compute_table_id(即源码中的_table_id)基于 project 名与 Feature View 名计算,因此测试中可以看到test_project_driver_dropoffs_stream这类表名(test_sqlite_plan.py)。

3.1 表结构

update()方法 与SqliteTable.update()中的 DDL 揭示了完整的表结构:

CREATE TABLE IF NOT EXISTS "<table_name>" ( entity_key BLOB, -- 序列化后的实体 key(EntityKeyProto -> 二进制) feature_name TEXT, -- 特征名 value BLOB, -- ValueProto 的 protobuf 序列化结果,读取时反序列化 value_text TEXT, -- 值的人读文本形式(过滤辅助列) value_num REAL, -- 可选列:仅 enable_openai_compatible_store 时创建,数值化形式 vector_value BLOB, -- 可选列:vector_enabled 时写入 f32 序列化向量 event_ts timestamp, -- 事件时间戳(UTC naive) created_ts timestamp, -- 物化/写入时间戳 PRIMARY KEY (entity_key, feature_name) )

并会为每张表额外创建entity_key索引:CREATE INDEX IF NOT EXISTS "<table>_ek" ON "<table>" (entity_key)

几个值得注意的实现细节:

  1. 复合主键(entity_key, feature_name)是"仅保留最新值"的根本保证。写入使用INSERT ... ON CONFLICT(entity_key, feature_name) DO UPDATE SET ...的 upsert 语句(online_write_batch L324-L328),同一实体同一特征的新值会整体覆盖旧值,而不是追加。
  2. entity_key以 BLOB 存储:写入前调用serialize_entity_keyentity_key_serialization_version版本序列化(L332-L335),读取时再反序列化。这也解释了为什么 SQLite 在线存储与 Go SDK 的 key 序列化版本必须保持一致。
  3. value存的是ValueProto的 protobuf 字节val.SerializeToString(),L345),读取时用ValueProto.ParseFromString还原(online_read L408-L409)。这一"protobuf 透传"方式使 Python 与 Go 两个 SDK 可以无损地共享同一份数据。
  4. 时间类型适配:模块顶部通过sqlite3.register_adapter注册了date/datetime的 ISO 8601 与 Unix epoch 两种适配,以及对应的 converter(L74-L111),配合建连接时的detect_types=PARSE_DECLTYPES | PARSE_COLNAMES(_initialize_conn L911-L929),使event_tscreated_ts列能以原生 Pythondatetime对象读出。
  5. 安全加固:所有表名/列名拼接前都会经过_quote_id的双引号转义,注释明确说明其目的是防止 SQL 注入。

四、写路径与读路径

4.1 写入:online_write_batch

online_write_batch 的写入流程是:

  1. 通过_get_conn懒加载连接(首次访问时才sqlite3.connect,且check_same_thread=False);
  2. 解析 Feature View 的特征类型字典、表名,并根据enable_openai_compatible_store检查表中是否已存在value_num列(结果会被缓存在_table_has_value_num中);
  3. 逐行执行参数化 upsert:entity_key序列化、时间戳统一转为 UTC naive(to_naive_utc),每个(entity_key, feature)对写入一行,value为 protobuf 字节、value_text为文本形式,可选写入value_num(数值)与vector_value(若该特征是向量类型,则按 Feature View 的vector_length元数据做serialize_f32序列化,默认长度 512);
  4. 整个批次包在单个with conn:事务中提交,并对每个实体调用progress(1)回调,供上层展示物化进度。

一个实用的行为差异:如果配置了enable_openai_compatible_store: true但表里还没有value_num列,源码不会报错,而是打印警告"Runfeast applyto add it. Writing without value_num."(L304-L309),即降级为不带数值列的写入。因此修改该开关后必须执行一次feast apply让 schema 变更落地。

4.2 读取:online_read

online_read 是一次典型的批量点查:

  • 把所有EntityKeyProto序列化成二进制后,用一条SELECT entity_key, feature_name, value, event_ts FROM <table> WHERE entity_key IN (?, ?, ...) ORDER BY entity_key一次性取回;
  • itertools.groupbyentity_key分组,逐实体重建feature_name -> ValueProto字典,并统一把时间戳规整为 UTC;
  • 未命中的实体返回(None, None),由上层在线服务将其解析为空特征值,而不是抛错。

Go SDK 侧的SqliteOnlineStore.OnlineRead采用完全一致的读法:mattn/go-sqlite3驱动打开同一文件,按 feature view 逐个执行IN (...)查询,用proto.Unmarshalvalue字节还原为types.Value,再按实体 key 的 hash 索引回填到结果矩阵。这也印证了功能矩阵中 "readable by Go: yes" 且 "readable by Java: no"(仓库 Go 目录存在独立实现,Java serving 模块则没有对应存储)。

五、基础设施生命周期:plan / update / teardown

SQLite Online Store 完整实现了OnlineStore接口的五个方法,功能矩阵中对应的四行 "update / teardown / plan: yes" 都能从源码逐一对应:

  • update()(L423-L458):对保留的表执行CREATE TABLE IF NOT EXISTS与索引创建;对需要删除的表执行DROP TABLE IF EXISTS。由于 SQLite 的ALTER TABLE ADD COLUMN不支持IF NOT EXISTS,源码封装了_alter_table_add_column_if_missing——捕获 "duplicate column name" 的OperationalError并静默忽略,其他错误照常抛出,以此实现幂等的列升级(value_textvalue_num)。
  • teardown()(L500-L522):比单纯 drop 表更彻底——先关闭连接,然后直接os.unlink删除整个.db文件,并对PermissionError做最多 10 次、间隔 0.25 秒的重试。这意味着feast teardown后数据库文件本身就不存在了。
  • plan()(L460-L498):从 registry proto 反解出全部 Feature View / Stream Feature View / 在线 Label View,为每一个生成一个SqliteTable基础设施对象(含pathnameinclude_value_num),序列化为SqliteTableProto(对应 protos/feast/core 下的SqliteTable.proto),供feast diff类命令做基础设施变更规划。值得注意的是,plan()必须区分FeatureView.from_protoStreamFeatureView.from_proto——回归测试 专门验证了"registry 中只有流特征视图"与"批、流视图混合"两种场景都能正确产出SqliteTable列表,这是一个曾经因类型检查导致崩溃的真实缺陷。

SqliteTable作为InfraObject的实现(L966 起),把CREATE TABLE / CREATE INDEX / ALTER逻辑收敛到update()中,并附带同样受 Python 版本约束的sqlite_vec加载逻辑,是feast apply在 SQLite 后端上的实际执行单元。

六、向量检索与全文检索扩展

虽然文档主体描述的是常规在线服务,但当前源码中 SQLite 后端还内置了两种检索扩展,对应配置项vector_enabledtext_search_enabled

6.1 向量相似检索(sqlite_vec)

开启vector_enabled: true后:

  • 写入时,向量类型的特征会被serialize_f32序列化为定长 float32 BLOB 存入vector_value列(L350-L365);
  • 检索入口retrieve_online_documents会动态创建vec0虚拟表、把目标向量字段的数据灌入,再用WHERE vector_value MATCH ? ORDER BY distance LIMIT ?做 KNN,最后 join 回主表取回entity_key、原始值与时间戳;
  • 从源码结构看,sqlite_vec扩展的加载被限制在Python 3.10sys.version_info[0:2] == (3, 10),L246-L252 与 L1014-L1021),且扩展缺失时仅告警降级,其他 Python 版本无法启用向量路径。
  • Feature View 中必须声明且仅声明一个带向量索引的字段,否则_get_vector_field会直接断言失败。

6.2 全文检索(FTS5 + BM25)与元数据过滤

  • 开启text_search_enabled: true后,retrieve_online_documents_v2会基于fts5创建search_tabletokenize="porter unicode61"),将 Feature View 的全部 STRING 类型特征列灌入,用bm25(search_table, ...)计分并ORDER BY distance LIMIT top_k完成关键词检索;
  • 若检索时携带数值条件过滤(filters),则必须同时开启enable_openai_compatible_store: true并运行feast apply生成value_num列,否则抛出明确异常(L688-L715)。过滤条件由SqliteFilterTranslator翻译成entity_key IN (SELECT entity_key FROM ... WHERE feature_name = ? AND value_num <op> ?)形式的子查询,支持eq/ne/gt/gte/lt/lte/in/ninAND/OR复合条件。

这些扩展能力使 SQLite 后端在本地场景下甚至可以承担轻量的"向量/全文 + 元数据过滤"型文档检索,但请注意其仍受单文件数据库的并发与容量限制约束。

七、功能矩阵与适用边界

以下是 官方文档 给出的完整功能矩阵(可直接对照 在线存储功能总览 与其他实现比较):

能力Sqlite
写入特征值到在线存储yes
从在线存储读取特征值yes
更新基础设施(如表)yes
拆除基础设施(如表)yes
生成基础设施变更计划(plan)yes
支持 on-demand transformsyes
可被 Python SDK 读取yes
可被 Java 读取no
可被 Go 读取yes
支持无实体(entityless)Feature Viewyes
支持同 key 并发写入no
支持检索时 TTLno
支持删除过期数据no
按 Feature View 共置yes
按 Feature Service 共置no
按实体 key 共置no

结合源码,可以把这几项 "no" 理解为工程取舍而非遗漏:

  • 无并发写:SQLite 的写锁与单文件模型决定了同一 key 的高并发更新不是它的设计目标;源码中连接仅懒加载一次并长期持有(_conn),面向单进程物化/服务场景。
  • 无 TTL / 无过期删除:因为 upsert 只保留最新值,时间维度信息仅体现在event_ts中,存储层不做过期清理。
  • 无 Java 读取:仓库的java/serving模块没有 SQLite 存储实现,只有 Python 与 Go(go/internal/feast/onlinestore/sqliteonlinestore.go)两条读取路径。
  • plan 能力独有优势:在总览矩阵中,支持 "generate a plan of infrastructure changes" 的核心实现只有 SQLite 与 Cassandra,这意味着 SQLite 后端可以完整地配合feast diff/feast apply做声明式基础设施演进。

适用建议:本地开发与学习(feast init默认形态)、快速演示与 notebook 实验(如 MCP Feature Store 示例、credit-risk 端到端示例 均使用 sqlite 在线存储)、CI 测试与离线评估;生产环境请换用总览矩阵中的其他在线存储。

八、参考文件索引

  • 官方参考文档:docs/reference/online-stores/sqlite.md;在线存储对比总览:docs/reference/online-stores/overview.md
  • Python 实现:sdk/python/feast/infra/online_stores/sqlite.py;向量配置基类:sdk/python/feast/infra/online_stores/vector_store.py
  • plan 回归测试:sdk/python/tests/unit/infra/online_store/test_sqlite_plan.py
  • Go 实现:go/internal/feast/onlinestore/sqliteonlinestore.go
  • 实际配置示例:examples/mcp_feature_store/feature_store.yaml;配置参考:docs/reference/feature-repository/feature-store-yaml.md

【免费下载链接】feastThe Open Source Feature Store for AI/ML项目地址: https://gitcode.com/GitHub_Trending/fe/feast

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 7:12:37

C++ Socket网络编程:从socket()到send()的核心原理与实战指南

先从我自己的经历说起。我第一次认真学C Socket&#xff0c;是在啃完某本C入门书之后。书里把网络编程放在很靠后的章节&#xff0c;二十来页把socket、bind、listen、recv、send全部带了一遍&#xff0c;然后扔了一个聊天室示例。代码能跑&#xff0c;但我心里完全不踏实&…

作者头像 李华
网站建设 2026/9/17 7:12:32

SpringBoot社区志愿服务系统设计与优化实践

1. 项目背景与核心价值社区志愿服务系统是连接公益组织、志愿者和服务对象的数字化桥梁。传统志愿服务管理面临三大痛点&#xff1a;志愿者调度效率低下、服务记录不透明、资源匹配不精准。我在参与某社区抗疫志愿服务时深有体会——组织者用Excel表格管理200多名志愿者&#x…

作者头像 李华
网站建设 2026/9/17 7:12:02

C++标准库std::expected实现与优化解析

1. 深入理解 C 标准库实现&#xff1a;从设计决策到优化技巧作为一名长期从事 C 开发的工程师&#xff0c;我最近在深入研究 libc 和 libstdc 中std::expected的实现差异时&#xff0c;发现了一些令人着迷的设计决策和优化技巧。本文将分享我在这个过程中的发现&#xff0c;特别…

作者头像 李华
网站建设 2026/9/17 7:11:18

AI系统提示词泄露风险与工程化防御指南

1. 项目概述&#xff1a;什么是 system_prompts_leaks&#xff1f;它为什么值得一线开发者警惕“system_prompts_leaks”——这个词组乍看像一串技术日志里的报错片段&#xff0c;但过去三个月里&#xff0c;它已悄然成为AI工程圈内高频复现的隐性风险信号。它不指向某个具体漏…

作者头像 李华
网站建设 2026/9/17 7:08:27

Colibri:基于NVMe与MoE的千亿参数大模型边缘推理系统

1. Colibri不是“又一个量化工具”&#xff0c;而是重新定义大模型推理边界的系统级工程你有没有试过在一台没有RTX 4090、甚至没有独立显卡的机器上&#xff0c;跑通一个参数量超过100B的MoE大模型&#xff1f;不是demo&#xff0c;不是token生成几下就崩&#xff0c;而是能稳…

作者头像 李华