关于 2026 年本地向量存储,最有教益的一个事实是:生产环境中调用量最大的那批检索系统,把自己的向量库删掉了——Claude Code 早期版本带过一个本地向量数据库,后来把它移除,改用 grep 检索。如果你的语料仍然需要索引——散文类语料确实需要——那么这四者并非四个互相竞争的产品,而是四种不同的架构;而选错形状,代价比选错牌子大得多。
一览
四者都在你自己的硬件上、在你自己的进程内运行。相似之处到此为止:一个是完全不带存储的库,一个是文件格式,一个是你多半本来就在用的数据库的扩展,还有一个是带预写日志的引擎。
| 项目 | 起始年份 | 它究竟是什么 | 数据存在哪里 |
| FAISS |
2017 |
一个相似度搜索库——C++ 加 Python 绑定,支持 CPU 与 GPU。 |
哪儿都不存。存储由你自己选择与管理。 |
| Chroma |
2022 |
一个嵌入式向量数据库,自 1.0 起内核为 Rust。 |
由引擎自己管理的一个持久化目录。 |
| LanceDB |
2023 |
架在 Lance 列式格式之上的一个检索引擎。 |
本地磁盘或对象存储上的 Lance 文件。 |
| sqlite-vec |
2024 |
一个纯 C 的 SQLite 扩展,新增 vec0 虚拟表。 |
就在你已经有的那个 SQLite 文件里。 |
许可协议在这里不构成区分点——FAISS 是 MIT,另外三个是 Apache 2.0,四者都能真正自托管且不回传数据。人气值得看一眼,但也仅此而已,因为它同时在度量三件不同的事:FAISS 积累了八年的研究者用户,而 sqlite-vec 才两岁,还没到 1.0。
它衡量的是年龄与受众,而非质量——FAISS 自 2017 年起就是参考级 ANN 库,而 sqlite-vec 只存在了两年。
请把"弱"的格子读成设计决策而非缺口:FAISS 是有意不做存储,sqlite-vec 是有意不做 ANN 索引,好让自己维持为一个零依赖的小 C 文件。
比选型更靠前的那个问题
在比较存储之前,值得知道的是:资源最充足的那批智能体团队看过这条流水线,然后掉头走开了。Anthropic 的 Claude Code 早期版本背后是一个本地向量数据库,后来把整套东西——嵌入模型、索引、分块启发式——统统换成了实时 ripgrep 调用。Cursor、Codex 与 Cline 在代码检索上也都倚重 grep 形态、可脚本化的检索。其理由是结构性的:当你在找 PaymentRetryPolicy 时,精确匹配就是正确;建在正被编辑的文件之上的索引,在写入与重建索引之间是陈旧的;而一次可以被收窄成另一次搜索的搜索,是一个计划,而不是一发子弹。
迄今最好的一次测量来自普华永道 2026 年 5 月的一篇 arXiv 论文《Is Grep All You Need? How Agent Harnesses Reshape Agentic Search》。在 116 道 LongMemEval 题目与若干"外壳/模型"组合上,当结果被内联注入上下文时,词法搜索一致胜过向量检索——某个 Claude Opus 配置下为 93.1% 对 83.6%,差距最小的一组是 76.7% 对 75.0%。但当同样的结果被写进文件让智能体自己去读、而不是拼接进提示词时,受测配置中有一半发生了排序反转。
值得带走的正是这次反转。检索质量并不只是检索器自己的属性;外壳与结果投递方式对数字的影响,与算法本身一样大。所以本文其余部分诚实的定位很窄:当语料庞大、以散文为主、且按语义而非按字符串来查询时,你需要索引——支持工单历史、合同、研究资料、政策文件;而当语料是代码形态且就在本地磁盘上时,你需要的是 grep。多数真实系统最后两者兼备。
FAISS — 深入
FAISS 返回整数 ID 与距离。让这些 ID 变得有用的一切,都是你要写的代码。
它如何存储
它不存储。这是关于 FAISS 最需要理解的一点,也是一半涉及它的对比文章分类错误的原因。没有文件格式、没有集合概念、没有元数据、没有持久化层——你在内存里建一个索引,而序列化它、重新加载它、以及决定崩溃之后怎么办,都是你的问题。这个库从没见过你的文本;它见到的是浮点数组,交回来的是整数。
它如何搜索
好得出奇,而且掌控力胜过本文其他任何一个。Flat 提供精确的暴力搜索;IVF 把空间划分成簇并只探查其中几个;HNSW 构建一张可导航的图;PQ 把向量压缩成编码,好让几十亿个也装得进内存。你可以把它们组合起来——IVF4096,PQ64 就是一个真实的索引描述——并亲手调节"召回—延迟—内存"这个三角。GPU 搜索是原生的,本文其他任何数据库都不具备。
它让什么变难
过滤,而那恰恰是生产环境真正的查询形状。"取 top-k,条件是 tenant_id = 7 且 lang = 'en'"并不是 FAISS 建模的东西;逃生阀是你从一张独立元数据表构造出来的 ID 位掩码,这意味着每个候选都要多一次间接寻址,以及两个绝不能互相矛盾的存储之间的同步问题。删除同样得手工来。如果你对"元数据放在哪儿?"的回答是"在 Postgres 里,我做个 join",那你刚刚描述的就是一个向量数据库的大部分,你该问问自己是不是本来就想要一个。
sqlite-vec — 深入
整个知识库就是一个文件——可以复制、可以随应用一起分发、也可以在浏览器里打开。
它如何存储
以行的形式,存在你已经有的那个 SQLite 文件里。CREATE VIRTUAL TABLE … USING vec0(…) 给你一张表,里面放着 float、int8 或二值向量,旁边可以有最多十六个可用于过滤的类型化元数据列、最多四个对索引做物理分片的分区键,以及承载负载但不建索引的辅助列。它是纯 C、零依赖的,所以能跑在 Linux、macOS、Windows、树莓派上,也能通过 WASM 跑在浏览器里——是本文唯一能随客户端应用一起分发的选项。
它如何搜索
靠扫描。默认是对分区内每一个向量做暴力 KNN,这听上去像缺陷,但多半是特性:召回是精确的、没有索引要建也没有参数要调,而且刚插入的行立即可被搜到,没有压实延迟。分区键是那个扩展杠杆——如果每次查询都限定在一个租户内,你扫的是那个租户的向量,而不是整份语料。官方建议是让每个分区键取值下有数百个向量,而不是寥寥几个,因为过度分片的代价大于收益。
它让什么变难
增长。线性扫描在数十万向量以内没问题,到一千万就很痛苦——那些把它排在 ANN 索引后面两三个数量级的基准,量的正是这一点,而且量得公道。这个项目也仍然明确处于 1.0 之前,预期会有破坏性变更;对一个由你掌控的文件来说这是可接受的风险,对一个不易迁移的 schema 来说则不是。作为交换,你免费拿到了本文最好的混合检索故事:FTS5 本来就在 SQLite 里,所以 BM25 加向量再加一次与权限表的联接,只是一条语句。
Chroma — 深入
写与读在设计上就是解耦的——这既是摄取快的原因,也是刚写入的行短时间内还搜不到的原因。
它如何存储
存在一个由它自己管理的持久化目录里,采用日志结构设计——1.0 的 Rust 重写让这一点变得明确。写入追加进一个不可变的预写日志并立即确认;一个后台压实进程把日志物化成段;读取查询这些段。这次重写宣称在写入与查询两侧都比此前的 Python 实现提升 3–5 倍,并带来了 JavaScript、Ruby 与 Swift 的原生绑定,以及面向浏览器的 WASM 构建。
它如何搜索
用 HNSW——一张常驻内存的可导航小世界图,这是有充分理由的标准选择:当索引装得下时,它在低延迟下有出色的召回。元数据 where 过滤在同一条查询路径上针对段元数据施加,而不是走一个独立存储;文档文本上还支持子串匹配。它没有 BM25 排序,所以在 Chroma 技术栈里说"混合检索",意味着关键词索引是你自己带来的。另外请注意:SPANN 这个磁盘友好的索引只存在于 Chroma 的分布式形态中,本文讨论的单机嵌入式模式里没有它。
它让什么变难
越过内存去扩展,以及察觉自己已经越过了。HNSW 常驻内存,所以单机上的实际天花板大约在一百万向量,再往上延迟就开始显出压力;越过之后的答案是分布式 Chroma——那是一个要运维的服务,而不是一个目录。另一处锋利的边缘是压实延迟:你刚写下的一行是持久的,但未必已经被索引,这一点在 notebook 里看不出来,在一个"写完立刻搜"的智能体循环里则令人困惑。
LanceDB — 深入
数据、元数据与索引是同一批文件——所以没有第二个存储要保持同步,也没有同步作业会出错。
它如何存储
以 Lance 列式格式,构建在 Apache Arrow 之上,存放在本地磁盘或对象存储上。这正是区分它的那次架构下注:引擎从内存映射文件里按列读取,而不是把索引攥在内存里,所以"语料大于内存"是常规情况而非故障情况。每次写入都产生一个可回读的新版本,这让"我在重新分块之前,检索是什么样子?"成为一个可回答的问题。列可以承载文本、向量、图像、音频或任意二进制块,这也是该项目围绕多模态数据做宣传的原因。
它如何搜索
默认用 IVF-PQ——倒排文件分区加乘积量化,一种压缩向量、能在普通硬件上扩展到极大集合的近似索引。向量索引旁边还有全文索引与标量索引,而查询 API 把向量检索、全文检索、SQL 式过滤与重排序组合成一次混合调用。这是四者中开箱即用最完整的检索方案,而它比裸 QPS 更要紧:在多数技术栈里,混合检索加重排序才是最大的那根质量杠杆。
它让什么变难
两件事,都很实在。IVF-PQ 是近似且量化的,所以召回是一个待调参数而非既定事实;独立基准显示,在收紧的元数据过滤下它的吞吐会下滑——那种困住多数分区型 ANN 索引的后过滤行为。另外,它的社区比 Chroma 或 Qdrant 小:教程更少、被回答过的问题更少,而且多进程并发访问有已记录的限制。买下一种磁盘原生格式,也就买下了它的生态。
横向对比
这个数据库有多少要你自己写
它们缺的东西在种类上不同:FAISS 缺的是数据库,Chroma 缺的是关键词那一半,sqlite-vec 缺的是索引。
四者构成一道干净的梯度。FAISS 交给你一个索引,把持久化、元数据、集合管理与删除完全留给你——大约四百行胶水代码,而且从此永远归你维护。sqlite-vec 从 SQLite 那里免费继承了存储、事务、联接与 BM25,于是周边代码几乎消失,但作为交换把扫描预算交到了你手上。Chroma 以最短的上手坡道供给存储、索引与过滤,然后悄悄把混合检索的关键词那一半留作习题。LanceDB 用一次调用供给得最多——向量、全文、过滤、重排序——代价是要你接受一种存储格式,以及一个更小的支持社区。
各自在哪里到头
其中三道天花板关乎内存,只有 LanceDB 那道关乎磁盘。
这里的规模上限由"索引住在哪里"决定,而不是由代码有多快决定。sqlite-vec 的暴力扫描是线性的,所以它的天花板来得最早——数十万以内从容,而当分区键让每次查询只触及一个租户的切片时还能再往外推。Chroma 的天花板是内存:HNSW 常驻内存,机器一旦吃紧就退化,在典型硬件上大约落在一百万向量。FAISS 靠量化与 GPU 搜索在技术上走得最远,但它的天花板是运维性而非算法性的——几十亿向量意味着你现在在运营一套系统。LanceDB 是唯一一个上限取决于存储而非内存的,这正是那次列式下注的全部意义,代价则是近似召回。
新鲜度,以及它在智能体循环里的后果
四者在写入之后的那几秒里表现不同,而智能体会以批处理流水线不会有的方式察觉到这一点。sqlite-vec 没有索引要更新,所以事务一提交,行就可被搜到——四者中最强的新鲜度保证,而当一个智能体写下一条笔记随即去搜它时,这是一项被低估的优势。Chroma 会立即向它的日志确认写入,但要等压实之后才可被搜到,于是"写完就读"的循环可能落空。FAISS 需要你显式地把向量加进索引并定期重建,所以新鲜度就是你代码实现出来的样子。LanceDB 每次写入都产生版本并做增量建索引,但新加入的行在索引追上之前是靠暴力搜索被检索的——那是一种优雅降级,而不是落空。
如何选
按"数据必须放在哪里"和"数据有多少"来选——而不是按某个没跑过你那些过滤条件的基准给出的 QPS 图表。
| 如果这描述的是你 | 选 | 因为 |
| 你的语料是本地磁盘上的代码、配置或一棵文档树 | 一个都不选 | 把 glob、grep 和 read 交给智能体。精确匹配、零索引维护、永不陈旧。 |
| 你的应用本来就带一个 SQLite 文件,或跑在设备端、浏览器里 | sqlite-vec | 知识库变成你本来就在备份的那个文件里的行,还免费附带 FTS5 混合检索与权限表联接。 |
| 你想今天下午就有能用的检索,之后再做决定 | Chroma | 从零到一个能用索引的最短路径,也是多数框架教程里的默认项,所以示例与你的代码对得上。 |
| 语料大于内存、或是多模态、或你想直接拿到混合检索加重排序而不用自己拼 | LanceDB | 磁盘原生的列式存储,向量、全文与标量索引都架在同一批文件上,一次查询搞定。 |
| 搜索本身就是那个难题——GPU、几十亿向量、自定义索引调参 | FAISS | 没有别的东西能给你这么多"召回—延迟—内存"三角上的掌控力。请为你将围着它写出来的那个数据库留预算。 |
| 你本来就在运维 Postgres | pgvector | 本地知识库并不必然意味着一个新依赖。向量与业务数据同行、一次事务、一套备份方案。 |
有一条告诫值得直说:无论你选哪个,限制答案质量的很少是存储本身,而是解析、分块与重排序。一个选了"错的"存储却加上本地交叉编码器重排序的团队,会胜过一个选了"对的"存储却只做纯稠密 top-k 检索的团队。
常见问题
如果智能体可以直接 grep,我还需要向量数据库吗?
对代码、配置或日志来说不需要——领先的编程智能体把自己的删掉了,效果反而更好。当语料以散文为主、按语义来查询时你才需要索引,因为用户的措辞永远不会与文档的措辞重合。多数生产系统两者并存,并在它们之间做路由。
这四个里哪个最快?
这个问题的欠定程度足以让基准变得有误导性。FAISS 在裸 ANN 吞吐上取胜,在 GPU 上尤其如此。sqlite-vec 在大集合上会慢两到三个数量级,因为暴力扫描是线性的;而在小集合上它恰恰正确,因为没有索引开销。LanceDB 在大规模、驻留磁盘的语料上很快,在收紧的元数据过滤下会下滑。Chroma 在索引还装得进内存之前一直很快。请拿你自己的过滤条件与语料规模去测,否则就别测。
我能先用一个、以后再换吗?
能,而且比听上去便宜——但前提是你保留分块并重新嵌入。除非嵌入模型完全相同,否则向量在不同存储之间没有可迁移的意义,而元数据 schema 总归要重塑。请把解析、分块之后的语料以一种持久格式作为事实来源保存下来,并把索引当作可重建的派生数据。仅这一条纪律,就让每一次存储选型都可逆。
sqlite-vec 的暴力搜索是致命伤吗?
只在超过你那个规模阈值之后才是。五万个分块时,一次全扫是毫秒级的,还免调参地给出精确召回;一千万时就没法用了。当查询天然限定在一个租户或一个项目内时,分区键能把这条线大幅外推。请核对你一年后实际会有多少向量,而不是基准里有多少。
那 pgvector、Qdrant 或 Milvus Lite 呢?
都很合理;而对任何已经在跑 Postgres 的团队来说,pgvector 是诚实的默认选项——服务端市场请看我们的 pgvector、Pinecone、Weaviate 与 Qdrant 对比。本文谈的是嵌入式的情形:没有服务进程、数据即文件、检索与智能体在同一个进程内。
该给它们配哪个嵌入模型?
几乎在所有情况下都用本地的——开源权重的嵌入模型在公开榜单上是直接领先的。Qwen3-Embedding 位居多语言 MTEB 榜首,高于主流托管接口;BGE-M3 是 MIT 许可的多语言主力,一次前向即产出稠密、稀疏与多向量输出;EmbeddingGemma 或 nomic-embed-text 占用远不到 1 GB。请把版本钉死:更换嵌入模型会让你存下的每一个向量作废。