如何用 sqlite-vec 做本地向量搜索:实用指南
2026/9/20 20:14:21 网站建设 项目流程

如何用 sqlite-vec 做本地向量搜索:实用指南

【免费下载链接】sqlite-vecA vector search SQLite extension that runs anywhere!项目地址: https://gitcode.com/GitHub_Trending/sq/sqlite-vec

文档库涨到二十万条之后,关键词检索的召回率已经难看。小站又不想为它单独部署一套向量数据库,这才遇到了 sqlite-vec:一个纯 C 实现、零依赖的 SQLite 向量搜索扩展,把单个动态库加载进任何 SQLite 进程,就能做 KNN 相似性检索。

最简接入姿势:从加载扩展到跑通 KNN

它的卖点用一句话讲清就是"哪里能跑 SQLite,哪里就能跑它":不用它,向量搜索意味着一个独立服务、一套运维、一份数据同步;用它,向量和业务数据待在同一个 .db 文件里,一条 SQL 就能参与事务。官方自称 "fast enough",不追极致性能,追的是迁移成本为零。

最短的验证路径是几行 Python(pip install sqlite-vec会同时装好绑定和扩展文件):

import sqlite3, sqlite_vec db = sqlite3.connect("docs.db") db.enable_load_extension(True) sqlite_vec.load(db) # 相当于执行 load_extension db.execute("create virtual table docs using vec0(doc_id int, e float[4])") db.execute("insert into docs(doc_id, e) values (1, '[-0.2, 0.25, 0.34, -0.21]')") q = "select doc_id, distance from docs where e match '[-0.2, 0.25, 0.34, -0.21]' and k=1" print(db.execute(q).fetchall()) # 输出 [(1, 0.0)] 即跑通

这段代码按顺序做了四件事:加载扩展、建向量表、插一条 4 维向量、再用 MATCH 语法找最近邻。注意向量这里传的是 JSON 字符串,绑定会自动转成紧凑的二进制格式落盘——4 个 float 只占 16 字节,比存 JSON 文本省得多。

vec0 虚拟表:把机制讲成人话

用 sqlite-vec 绕不开的概念是 vec0 虚拟表,可以把它理解成一个"向量书架":建表时扩展会在内部自动维护几张影子表存放向量(结构见 ARCHITECTURE.md),查询时先按 rowid 定位、再直接取出对应向量,而不是逐行扫描。这也是 vec0 比"把向量当普通 BLOB 列存、用标量函数暴力扫"更快的原因——不过后者同样受支持且更灵活,官方 KNN 文档里两种方式都有示例。

距离度量值得花十秒搞清楚:vec0 默认用 L2(欧氏距离),衡量的是"两点隔多远";建表时声明distance_metric=cosine则改测"方向差多少"。做文本 embedding 检索时 cosine 通常更稳,因为关心的是语义方向是否一致,而不是向量本身的长短。

分区键:降低查询延迟的第一招 ⚠️

数据涨到几十万行后,全表 KNN 扫描开始有点吃力。而业务数据本来就按租户隔离,大多数查询只针对单个租户——这正是 partition key 的用武之地:

create virtual table docs using vec0( tenant_id int partition key, e float[768] ); select doc_id, distance from docs where tenant_id = 42 and e match :query and k = 10;

这两段的意思:向量按 tenant_id 分区存放,查询时带上tenant_id = 42条件,扩展就只扫描该分区内的向量。改动之后单租户查询的耗时明显下降,因为参与比较的向量数从全量变成了租户内那一份。这里要注意两点:分区键最多 4 列,且只能是整型或文本,塞不进任意表达式。

三个容易栽的坑

第一个是扩展加载。Python 的 sqlite3 默认禁用 load_extension,漏掉示例里那行enable_load_extension(True)会直接抛异常;走 JDBC(Xerial 驱动)也一样,得先调用对应的启用方法再 loadExtension。传给加载函数的应该是扩展文件本身(vec0.so / vec0.dll),传成目录名会得到一个误导性的"文件不存在"。

第二个是维度一致性。建表声明float[768]就必须插 768 维向量,维度不匹配时的报错不太友好,很容易先怀疑自己的数据。我一般在生成脚本里把维度写成常量,并在入库前对 embedding 模型的输出维度做一次断言。

第三个是 k 的写法有版本坑:and k = 10这种写法在所有 SQLite 版本都有效,而直接limit 10只在 SQLite 3.41+ 生效——用系统自带旧版 SQLite 的机器上会静默失效,统一用 k 就安全。

适用边界:什么时候选它,什么时候放弃

如果你的场景是本地工具、端侧应用、小站点的旁路服务,数据量在百万级以内,没有 GPU 也不打算运维任何向量数据库,sqlite-vec 几乎是唯一合理选项,它甚至能在浏览器(WASM)里跑通同样的查询。反过来,如果是亿级向量、需要 ANN 索引召回和水平扩展,或者写入并发很高,直接上 Faiss、Milvus 或 Qdrant——那是另一类机器。sqlite-vec 的 "fast enough" 是暴力扫描,小数据量下这是优点,大数据量下就是天花板,认清这条线再选型。

【免费下载链接】sqlite-vecA vector search SQLite extension that runs anywhere!项目地址: https://gitcode.com/GitHub_Trending/sq/sqlite-vec

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询