存向量这件事本身很简单,难的是带着过滤条件、在可接受的延迟内、用能负担得起的内存,找到”差不多最近”的那几条。索引结构解决前半句,工程能力解决后半句。
一、向量存储在系统里承担什么
从调用方的角度看,向量存储要提供的接口只有几个:写入一批向量和它的元数据、按某个向量找最近邻、带过滤条件找、删除。看起来很薄,实际上每个都能展开成一堆取舍。
它和普通数据库的差别在于”最近邻”这件事只能近似。精确的 k 近邻要与每个向量算一遍距离,复杂度 O(N·d),一千万条 1024 维向量就是一万亿次乘加,任何硬件都吃不下。于是全部转向近似最近邻(ANN),用一小部分召回率换几个数量级的加速。
这带来一个必须一开始就建立的认知:向量检索的召回率和延迟不是调优问题,而是配置问题。同一份数据、同一个索引,把参数调大一档,召回上升、延迟上升、内存不变;换一种索引结构,则是另一种形状的取舍曲线。没有”最优配置”,只有”在给定资源和延迟预算下的最优点”。
规模这一维值得单独说,因为每跨一个数量级,工程重心都会变:
| 规模 | 工程重心 |
|---|---|
| 十万以内 | 别引入新组件,用已有的数据库加扩展就够 |
| 百万级 | 索引参数与内存预算开始重要,量化值得考虑 |
| 千万级 | 单机内存开始吃紧,要上磁盘索引或分片 |
| 亿级及以上 | 变成分布式存储与运维问题,选型空间收窄到少数几个系统 |
二、三种索引结构
近邻检索的索引结构经过几十年演进,工程上真正在用的收敛成三类。

暴力检索
不算索引,就是把所有向量存下来,查询时逐个算距离。召回率百分之百,结果精确,延迟随数据量线性增长。
它的工程价值在于两点:一是给其他索引当”精确率基准”,用来量近似索引到底丢了多少召回;二是十万条以内的规模,它常常比建索引更快,省掉了构建时间和图结构的内存开销。很多向量库把它叫 FLAT 或 brute force。
聚类加倒排:IVF 家族
先用 k 均值把向量聚成 nlist 个簇,每个簇有一个中心点,所有向量按所属簇存进倒排列表。查询时只算查询向量与簇中心的距离,选最近的 nprobe 个簇,只在簇内做精确比较。
复杂度从 O(N·d) 降到大约 O(nprobe/nlist × N·d)。nlist 和 nprobe 是唯一的两个旋钮:nlist 越大簇越细、召回越依赖 nprobe;nprobe 越大召回越高、延迟越高。
IVF 的两个工程约束要注意。一是需要训练:簇中心由数据决定,所以建索引前要先跑一遍聚类,训练数据的质量和数量会影响索引效果。二是边界效应:正确答案如果落在相邻簇的边界上,而那个簇没被选中,就永远召不回来——这不是参数没调好,是结构本身的特性。
在 IVF 之上叠一层乘积量化就得到 IVF-PQ:把向量切成若干子空间,每个子空间用少量比特编码成聚类中心的编号,于是向量被压成几十个字节。压缩比可以做到十几到几十倍,代价是距离计算变成查表近似,召回进一步下降。
图:HNSW 与它的家族
HNSW 是分层可导航小世界图。每个点在第 0 层有最多 2M 条连边,上层最多 M 条;上层点稀疏、边长长,用来做粗跳转,下层点密、边短,用来做精确定位。查询从最上层的入口开始,每层贪心走到局部最近的点,再下沉到下一层,像跳表一样逐层细化。
三个参数决定它的行为:
| 参数 | 作用 | 调大的代价 |
|---|---|---|
| M | 每个点的连边数 | 内存和构建时间上升,召回上升 |
| ef_construction | 建图时的候选队列长度 | 建索引变慢,图的质量变好 |
| ef_search | 查询时的候选队列长度 | 延迟上升,召回上升 |
它的优点很直观:查询接近对数复杂度,召回率通常明显高于同规模的 IVF,而且是纯内存结构、不依赖训练。缺点同样直接:图结构吃内存,而且删除很别扭——删掉一个点会破坏图的连通性,所以多数实现是打墓碑标记,查询时跳过,空间不回收,到一定程度需要重建。
内存这部分值得算清楚,因为它常被误解成”很大”。图结构的开销主要在第 0 层的连边:每个点最多 2M 条边、每条边存一个 4 字节的节点编号,M 取 16 时就是约 128 字节/点;上层贡献的连边总数与点数同量级,可以忽略。一百万点的图结构约 128 MB,而 1024 维单精度向量本身是 4 GB——图只占百分之三。但如果把向量量化到每点 1 KB,同样的图就占到 12%,这时候它就不能再被当成零头。
三类结构的对比:
| 结构 | 查询复杂度 | 内存 | 是否需训练 | 增删改 | 典型召回 |
|---|---|---|---|---|---|
| 暴力(FLAT) | O(N·d) | 仅向量 | 否 | 自由 | 100%(精确) |
| IVF / IVF-PQ | 约 O(nprobe/nlist·N·d) | 低 | 是 | 较自由 | 中到高 |
| HNSW | 约 O(log N) | 高 | 否 | 插入容易,删除要墓碑 | 高 |
还有一类结构面向单机放不下十亿级的场景:磁盘索引(DiskANN 一线)。它把图和向量放在 SSD 上,靠精心设计的布局把随机读次数压到可接受的范围,用延迟换容量。当内存装不下全量向量时,这是比”加机器”更常见的选择。
参数的起点值
索引参数的默认值通常偏保守,直接上线会得到”能用但不够好”的结果。下面这些是有据可依的起点,实际值要靠自己的数据量出来。
| 索引 | 参数 | 起点 | 调整方向 |
|---|---|---|---|
| HNSW | M | 16(通用)、32 到 64(高召回要求) | 调大:召回升、内存升、构建变慢 |
| HNSW | ef_construction | 200 | 调大:图质量更好、构建更慢 |
| HNSW | ef_search | 40 到 64 起步,按召回目标最高调到 200 上下 | 调大:召回升、延迟升 |
| IVF | nlist | 取 √N 的若干倍(常见做法在四倍上下) | 调大:簇更细,需要同时提高 nprobe |
| IVF | nprobe | 从 nlist 的百分之一量级起 | 调大:召回升、延迟升 |
| PQ | 子空间数 | 每子空间放 4 维(即维度除以 4) | 子空间越多越准,压缩比越低 |
| PQ | 每子空间比特 | 8 | 调大:更准、更占内存 |
调参的经验顺序是:先固定 ef_search 测召回曲线,确定达标所需的最小值;再用这个值去测延迟是否满足预算;两者冲突时回头改索引结构或量化方式,而不是在两个参数之间反复拉锯。
稀疏向量与倒排
词法检索的倒排索引本身就是一种稀疏向量检索:词表维度可以是几万到几十万,每个文档只有其中很少的维度非零。学习到的稀疏表示(SPLADE 一线)把这种倒排索引用了起来,只不过权重由模型学出来,还支持把查询扩展成相关词。
它的价值在于和稠密检索互补:稠密向量擅长语义泛化,稀疏表示对精确词、罕见词、编号这类 token 更敏感。多数向量库对稀疏向量的支持和稠密向量不在一个层次上,接入前要确认清楚——能存稀疏向量不等于能高效地做稀疏检索。
三、量化:把内存降下来
向量本身是内存的大头,所以压缩的收益最直接。常见做法按压缩比排序:
| 方式 | 每点内存(1024 维) | 相对单精度 | 召回影响 |
|---|---|---|---|
| 单精度 fp32 | 4 KB | 1× | 基准 |
| 半精度 fp16 | 2 KB | 2× | 几乎无损 |
| 标量量化 int8 | 1 KB | 4× | 很小 |
| 二值量化 | 128 字节 | 32× | 明显,需要重排补偿 |
| 乘积量化 | 8 到 64 字节 | 64 到 500× | 中等,取决于子空间数与比特数 |
百万级向量、1024 维的账很好算:单精度 4 GB、半精度 2 GB、int8 1 GB、二值 128 MB。这就是为什么”内存不够先量化”是对的默认动作,加机器的性价比通常不如压缩。
二值量与乘积量化都会让距离估计失真,标准的补偿方式是两段式检索:先用压缩向量召回一个较大的候选集(比如 200 条),再用原始向量(或半精度向量)对候选重算距离并取前 k 条。原始向量要不要留在库里,是选型时要确认的一个能力点——有些系统压缩后不保留原向量,就无法做这一步补偿。
四、过滤:最容易被低估的一块
业务查询几乎都带条件:只看某个租户的、只看最近一年的、只看已发布版本的。过滤和向量检索的交互方式有三种,代价差别很大。
先过滤再检索(pre-filter)。先用条件筛出候选集合,再在集合内做向量检索。召回率理论上最高,但如果条件筛完只剩几百条,向量索引就没意义了;更麻烦的是多数图索引无法在”只包含部分点”的子图上高效遍历。
先检索再过滤(post-filter)。先按向量找出 top-k 再筛条件。实现最简单,但结果数不可控——pgvector 的文档里给过一个很清楚的例子:条件只匹配 10% 的行、HNSW 的 ef_search 取默认的 40 时,平均只有 4 行能通过过滤。用户要的是 10 条,返回 4 条,看起来就是”检索坏了”。
过滤感知的检索(in-filter)。把谓词下沉到索引遍历里,图在搜索时会跳过不满足条件的点,但仍然沿着连通性继续走,直到凑够候选。这是效果最好的做法,也是实现难度最高的:图索引需要专门设计(ACORN 一线就是这个方向的工作),倒排类索引相对容易。
工程上的处理手段有这么几种,可以组合用:
- 提高候选量。把召回数从 50 提到 200 或 500,让过滤后有足够的存活结果。代价是延迟和重排成本
- 迭代式扫描。pgvector 从 0.8.0 起支持这个模式:过滤后行数不够时自动继续往索引深处扫描,直到凑够结果或达到上限。这是”先检索再过滤”的补救
- 把高频过滤字段做成物理结构。按租户分区、按时间分表、按类型建独立集合,让过滤在进入检索前就完成
- 候选集放大加二次筛选,用于权限这类会变化的标签
判断自己是否踩了这个坑,有个简单的办法:统计”返回结果数少于请求数”的请求占比。这个比例高,说明过滤和检索在打架,调索引参数没有用。
五、度量与数据类型
距离度量必须和嵌入模型的训练目标一致,这是最容易出错又最难发现的一类问题。
余弦相似度和内积在向量归一化之后是等价的:
$$\cos(\mathbf{q}, \mathbf{d}) = \frac{\mathbf{q} \cdot \mathbf{d}}{\lVert \mathbf{q} \rVert \lVert \mathbf{d} \rVert} = \mathbf{q}_{\text{norm}} \cdot \mathbf{d}_{\text{norm}}$$
所以入库时统一做归一化、距离度量选内积或余弦,能消掉一层不确定性。欧氏距离则在归一化后有单调关系 $\lVert \mathbf{q} - \mathbf{d} \rVert^2 = 2 - 2\cos(\mathbf{q}, \mathbf{d})$,排序结果一致,但不等价于可以为所欲为地混用——很多库把”内积”和”余弦”实现成两套代码路径,选错会得到完全无意义的结果,而且不报错。
维度上限是另一个容易撞上的墙。不是所有库都能索引任意维度的向量,pgvector 的清单很典型:
| 类型 | 可索引的最大维度 | 说明 |
|---|---|---|
| vector(单精度) | 2000 | 超了只能存不能索引 |
| halfvec(半精度) | 4000 | 高维模型的常见出路 |
| bit(二值) | 64000 | 配合二值量化使用 |
维度越高的模型(比如 3072 维、4096 维)越容易撞上限。有的模型支持套娃式截断(先按低维检索再补算),有的库支持降维索引,这些都要在选型时确认。
六、选型要看的能力清单
索引类型和性能只是选型的一部分,真正会让项目卡住的往往是下面这些。
| 能力 | 要确认什么 | 踩坑后果 |
|---|---|---|
| 过滤方式 | 是 pre、post 还是过滤感知;是否支持迭代扫描 | 过滤后结果数不足,召回断崖 |
| 更新语义 | 是否支持原地更新;删除是真删还是墓碑;空间回收方式 | 大量更新后索引退化、空间不释放 |
| 一致性与持久化 | 是否有 WAL、崩溃恢复、与业务库的一致性方案 | 重启丢数据、向量与业务数据不一致 |
| 多租户 | 集合隔离、分区、行过滤,以及租户数量上限 | 租户多时元数据或连接数爆炸 |
| 混合检索 | 是否内置全文检索与融合;稀疏向量支持到什么程度 | 要把两套系统拼起来,一致性自己解决 |
| 量化与重排 | 压缩后是否保留原始向量 | 无法做二段式重排补偿召回 |
| 扩展方式 | 单机到分布式的路径、分片与副本、扩容是否需要停机 | 到千万级才发现要重搭 |
| 生态与迁移 | SDK 完整度、与编排框架的集成、导入导出的便利程度 | 迁移成本高于预期 |
| 许可证 | 商用限制、多租户 SaaS 限制、品牌条款 | 商用后被迫重构 |
| 成本结构 | 内存型还是磁盘型;托管是按节点还是按用量 | 账单随数据量非线性上涨 |
七、主流系统逐个说
下面按类别过一遍目前主流的选择。定位和许可证取自各仓库当前状态(2026 年 10 月)。先给一张横向总表,细节在后面。
| 系统 | 许可证 | 索引 | 过滤 | 适合规模 | 运维成本 |
|---|---|---|---|---|---|
| pgvector | PostgreSQL | HNSW、IVFFlat | SQL WHERE 加迭代扫描 | 百万到千万 | 无(挂在现有库上) |
| pgvectorscale | PostgreSQL | DiskANN 思路 | 同上 | 千万到亿 | 低 |
| Qdrant | Apache 2.0 | HNSW(过滤感知) | 过滤下沉进图遍历 | 千万 | 中 |
| Milvus | Apache 2.0 | HNSW、IVF、DiskANN、SCANN、GPU | 标量索引加范围检索 | 亿级以上 | 高 |
| Weaviate | BSD-3 与自有许可混合 | HNSW、FLAT | 过滤与混合检索内建 | 千万 | 中 |
| Vespa | Apache 2.0 | HNSW | 搜索出身,能力完整 | 千万到亿 | 中高 |
| Elasticsearch | Elastic License 2.0 与 SSPL | HNSW | 极强(倒排加列存) | 千万 | 中,已有则低 |
| OpenSearch | Apache 2.0 | HNSW | 同上 | 千万 | 中 |
| Redis | 分段变化,见下文 | HNSW、FLAT | 有限 | 百万 | 低 |
| LanceDB | Apache 2.0 | IVF-PQ、HNSW | 支持 | 单机磁盘容量内 | 极低 |
| Chroma | Apache 2.0 | HNSW | 基础 | 十万以内 | 极低 |
| Typesense | GPL-3.0 | HNSW | 强(搜索出身) | 百万到千万 | 低 |
| Meilisearch | MIT | HNSW(较新) | 强 | 百万级 | 低 |
| ClickHouse | Apache 2.0 | HNSW(25.8 起) | 极强(SQL) | 千万,偏分析 | 中 |
| Apache Doris | Apache 2.0 | HNSW、IVF、IVF On-Disk | 先过滤再检索,要求二级索引 | 千万,偏分析 | 中 |
| DuckDB VSS、sqlite-vec | MIT、Apache 2.0 | HNSW、暴力 | 基础 | 十万以内 | 无 |
数据库型
pgvector(PostgreSQL 许可)。Postgres 的向量扩展,支持 HNSW 与 IVFFlat 两种索引,向量类型有单精度、半精度、稀疏、位四种,索引维度上限分别是 2000、4000、64000。它最大的优势不是性能,而是向量和业务数据在同一个库里:同一次事务写业务表和向量表,过滤条件就是普通的 SQL WHERE,权限体系、备份、监控全部复用。从 0.8.0 起支持迭代式索引扫描,缓解了过滤后行数不足的问题。
适合的规模是一百万到一千万条向量,判断依据是单表索引的内存与构建时间。要注意的是维度上限、以及过滤条件的选择性——选择性差的条件会让 Postgres 的查询计划在向量索引和常规索引之间选错,需要人工干预。
配置上有几个关键项值得记住:
SET hnsw.ef_search = 64; -- 查询期召回与延迟的主要旋钮
SET hnsw.iterative_scan = strict_order; -- 过滤后结果不足时继续向索引深处扫描
SET hnsw.max_scan_tuples = 20000; -- 迭代扫描的上限,防止一次查询扫太久
SET ivfflat.max_probes = 100; -- 用 IVFFlat 时对应的上限
建索引时不要漏掉并行:CREATE INDEX ... USING hnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 200) 配合 max_parallel_maintenance_workers,是百万元素级构建时间的主要差别来源。
配套的 pgvectorscale(PostgreSQL 许可)用 DiskANN 思路的索引把单机容量推到更大规模,是 pgvector 内存不够时的自然升级路径。
Qdrant(Apache 2.0)。Rust 实现,主打”过滤与向量检索一起做好”:过滤条件在 HNSW 遍历中生效,不需要靠放大候选来补。支持稠密、稀疏、多向量三种表示,支持标量、乘积、二值三种量化,也能把向量放在磁盘上。单机性能强,分布式是分片加副本的常规设计,运维复杂度明显低于 Milvus。默认选择里最”没有明显短板”的一个,适合需要强过滤、千万级规模、又不想扛分布式运维的场景。
Milvus(Apache 2.0)。云原生分布式向量库,把系统与检索引擎分离,支持 HNSW、IVF、FLAT、SCANN、DiskANN 以及各自的量化变体,还支持 mmap 降低内存占用和 GPU 索引(NVIDIA CAGRA)。元数据有标量索引、支持范围检索。它的定位是亿级及以上:组件多(依赖 etcd、对象存储、消息队列),运维成本高,换来的是水平扩展能力和完整的周边(GUI、CDC 同步、监控、Spark/Kafka 连接器)。
Weaviate(BSD-3 与自有许可混合)。模块化设计,可以内置向量化模块(在库侧调用嵌入模型),内置 BM25 与向量混合检索并在服务端完成融合,多租户是内建能力,向量压缩支持量化与多向量编码。适合希望少写编排代码、把混合检索和租户隔离交给数据库的场景。要留意的是许可:主体是 BSD-3,部分企业功能在自有许可下;某些版本的许可调整也发生过,商用前读一眼 LICENSE。
Vespa(Apache 2.0)。老牌的检索引擎,向量只是它能力的一部分。它真正的强项是排序表达式:可以把向量相似度、BM25 分数、业务字段、机器学习模型输出写进同一个排序函数里,做高度定制的相关性排序。实时写入与检索能力强,适合”排序逻辑复杂、需要在线调整权重”的场景。代价是概念多、学习曲线陡。
Elasticsearch / OpenSearch。全文检索领域的老牌选手,向量是后加的能力:dense_vector 字段配 HNSW 索引,支持量化压缩,能与 BM25 做混合检索。过滤能力是它相对向量库的绝对优势——倒排索引与列存支持的复杂条件、聚合、排序都是成熟的。许可证需要分清:Elasticsearch 从 7.11 起使用 Elastic License 2.0 与 SSPL 双许可,对外提供托管服务有约束;OpenSearch 是它的 Apache 2.0 分支,能力接近但生态略小。已有 ES 的团队用它起步通常比引入新组件划算。
Redis(许可情况复杂)。内存数据库加向量检索(HNSW 与 FLAT),延迟极低,适合缓存与向量检索共存的场景。许可证要特别看清楚:7.2 及以前是 BSD-3,7.4 到 7.8 是 RSALv2 或 SSPLv1 二选一,8.0 起提供 AGPLv3 选项;另外部分 Intel 优化(Leanvec 之类)是闭源的、只在 RSALv2 下可用。内存成本高是它作为向量库的固有短板。
LanceDB(Apache 2.0)。嵌入式向量库,底层是 Lance 列式格式,向量和原始数据(图片、音频、文本)放在一起,支持 IVF-PQ 与 HNSW。定位是本地与边缘:单进程、零运维、直接从磁盘检索,适合桌面应用、单机分析、以及把向量数据当作”带索引的数据文件”来管理的场景。
Chroma(Apache 2.0)。开发体验优先的嵌入式向量库,API 简单,在原型和教学场景里出镜率很高。定位是单机与中小规模,把”带过滤的向量查询”做得很顺手。生产环境大规模使用前建议评估它的持久化与并发能力。
Typesense / Meilisearch。轻量全文检索引擎,都在近两年补上了向量与混合检索。Typesense 采用 GPL-3.0,它的文档里专门解释了理由:GPL 对”作为独立服务运行”的用法是宽松的,只有当修改并分发时才触发义务;如果要把引擎代码嵌进自己的程序里,这条许可就要重新评估。Meilisearch 是 MIT,语义检索与混合检索是较新的能力,适合从全文检索起步、逐步加上语义的路径。
ClickHouse(Apache 2.0)。分析型数据库,25.8 起提供原生的向量相似度索引(基于 usearch 的 HNSW)。它的价值在分析场景:先用 SQL 做大规模过滤与聚合,再对筛出来的子集做向量检索,或者反过来把向量检索结果与业务指标做关联分析。不适合当作纯粹的向量库来用。
Apache Doris(Apache 2.0)。MPP 架构的实时分析数据库,4.0 起原生支持 ANN 向量检索,底层基于 Faiss,索引有 HNSW、内存版 IVF,以及把倒排列表放磁盘、靠缓存服务的 IVF On-Disk 三种。向量以定长数组存储,没有引入新的数据类型。它和 ClickHouse 属于同一类:向量是分析引擎的一个扩展,价值在于向量检索、关键词检索和结构化分析写在同一条 SQL 里——倒排索引做过滤、ANN 做语义召回、聚合与关联分析都在同一个引擎内完成。
几个实现细节值得记:它采用先过滤再检索(pre-filter)语义,代价是过滤字段必须建过二级索引(例如倒排索引),否则会退化成暴力计算以保证结果正确;支持按距离范围检索,也能和 TopN 组合;量化有标量(int8、int4)和乘积量化,官方给出的对比里,768 维百万级数据集上 int8 把索引从 3.1 GB 压到 1 GB 左右。约束上有两点要注意:ANN 索引只能建在 Duplicate Key 表模型,或者开启 merge-on-write 的 Unique Key 表上;查询时距离函数必须和索引的度量类型一致,内积还必须配合降序排序才会走索引。
官方文档里还贴了一组压测数据(32 核 64 GB 双节点、百万级 768 维数据集):240 并发下约 3300 QPS、平均延迟 71 毫秒、召回率 91%;换成 int8 量化后召回降到 88%,暴力检索则只有不到 4 QPS。厂商公布的数据不能直接替代自测,但这组数字的量级能说明它的定位——它不是要和专用向量库比单点性能,而是把”分析库已有的数据”直接变成可检索的向量库。
部署形态上,它的经典架构是存算一体:只需要部署 FE 和 BE 两类进程,不依赖外部对象存储或共享文件系统,一套集群自己管自己的存储。3.0 起增加了存算分离模式,元数据与数据落到对象存储、计算节点无状态,换来弹性伸缩和成本优化。对向量检索而言这个区别的实际影响是:存算一体模式下索引常驻本地内存,延迟更可控;存算分离模式下要关注索引缓存的命中率,冷启动后的第一次查询会明显更慢。
DuckDB 与 SQLite 系。嵌入式数据库的向量扩展:DuckDB 的 VSS 扩展提供 HNSW 索引,SQLite 生态里有 sqlite-vec 这类扩展。适合数据量不大、希望”零依赖、单文件”的场景,比如本地工具、离线分析、桌面上跑的检索。
服务化的库(不是数据库)
FAISS(MIT)、hnswlib(Apache 2.0)、DiskANN(MIT)、cuVS(Apache 2.0)、USearch(Apache 2.0)这些是索引库而不是数据库。它们只提供”建索引、查近邻”的能力,持久化、并发控制、过滤、更新、监控都要自己写。适合两类场景:研究评测(换索引结构做对比),以及把索引嵌入到自己系统里的深度定制。直接拿它们当服务层用,通常会在元数据管理、崩溃恢复、在线更新这些地方反复踩坑。
Annoy(Apache 2.0)值得单独提一句:它是早期广泛使用的内存型索引,只读、适合构建后不再变化的场景,但仓库更新已经很慢,新项目不建议选它。
托管服务
Pinecone。托管向量库的代表,serverless 架构把存储与计算分离,按用量计费,不需要关心分片和副本。适合不想运维、快速验证的场景;数据出境、长期成本、厂商锁定是三个要在决策里写明的问题。
Zilliz Cloud(Milvus 的商业版)、Azure AI Search(内置混合检索与语义排序)、MongoDB Atlas Vector Search(向量与文档数据同库)、AWS 的 OpenSearch Serverless 与 Aurora pgvector 属于同一类:把开源自建形态做成了托管。如果已经在用对应的云生态,这类服务往往是阻力最小的选择。国内云厂商(阿里云、腾讯云、百度等)也都有托管向量检索服务,合规和网络延迟上有优势,接口与生态的成熟度需要单独评估。
八、按场景选
把上面的内容压成一条决策路径:
- 已经在用 PostgreSQL、数据量在千万以内:直接上 pgvector,不引入新组件。内存不够就加 pgvectorscale 或开量化
- 已经在用 Elasticsearch/OpenSearch、且全文检索是刚需:用它的向量能力做混合检索,不要为了向量再引一套系统
- 过滤条件复杂、需要千万级单机:Qdrant 是默认答案,过滤感知是它区别于通用向量库的地方
- 亿级以上、团队有分布式运维能力:Milvus 或直接买托管
- 本地、边缘、桌面应用:LanceDB、DuckDB VSS、sqlite-vec 这类嵌入式方案
- 数据本来就在分析库里(Doris、ClickHouse):用引擎自带的向量能力,省掉一次数据搬运和一条同步链路,代价是索引能力不如专用库
- 排序逻辑复杂、要在线调权重:Vespa
- 完全不想运维、且合规允许:托管服务
- 只是做算法评测:FAISS 或 hnswlib,别搭服务
反过来的三种反模式也值得记住:为十万条向量上分布式系统;先定向量库再定嵌入模型(两者耦合,换了模型要全量重建);只看索引性能不看过滤能力(过滤才是生产环境里的主要矛盾)。
九、容量与性能怎么算
动手前先把内存算清楚,这是唯一能在选型阶段精确计算的指标。
$$\text{内存} \approx N \times d \times b_{\text{向量}} + N \times b_{\text{图}} + \text{元数据开销}$$
举个例子:一亿条 1024 维单精度向量,向量本身 400 GB,图结构(M 取 16,约 128 字节/点)再加 13 GB。这个量级决定了必须走量化或者磁盘索引——量化到 int8 是 100 GB,二值量化到 128 字节/点是 13 GB,加上图结构的 13 GB 只有 26 GB,单机就能放下。
延迟这一侧无法靠公式给出可信答案,只能实测。要测的项有四个:
- 召回率。拿黄金查询集,用暴力检索做基准,量近似索引到底丢了多少。这一项必须自测,任何厂商的公开数据都不能替代
- p50 与 p99 延迟,以及在目标 QPS 下的延迟曲线(多数系统在低并发下延迟很好看,到 50% 负载时会翻倍)
- 过滤条件下的召回与延迟。用真实的选择性分布测(比如选择性 10%、1%、0.1% 各测一组),这是最容易出问题的一组数据
- 索引构建时间与内存峰值。构建过程往往比查询更吃资源,在线扩容时的构建时间直接决定运维窗口
十、运维
索引构建。大批量导入后再建索引,比边导入边建快得多。构建过程要预留内存峰值,多数实现会同时持有原始数据和图结构。能做到并行构建的(pgvector 支持并行建索引)要把并行度利用起来,构建时间常常是小时和分钟的区别。
增量写入与退化。图索引在持续插入后会退化:新点只和邻近的少数点连边,逐渐形成”孤岛”,召回率缓慢下降。缓解办法是控制写入模式(尽量批量)、定期重建、以及在写入量大的系统里把索引分成”历史段”与”增量段”。
删除。多数实现的删除是墓碑标记,空间不回收,需要定期重建或触发空间的整理。删除率高的系统要专门设计这个流程,否则磁盘和内存会只涨不降。
一致性。向量库和业务库的双写很难保证原子性。常见的做法是以业务库为准,用变更日志(CDC 或消息队列)异步同步到向量库,并保证同步是幂等的(按内容哈希或版本号去重);对一致性要求高的场景,接受”检索结果可能陈旧几秒”并在产品上明说。
迁移与升级。索引格式会随版本变化,大版本升级常常需要重建索引;更换嵌入模型更是必然全量重建。把”重建”设计成常规能力:保留原始片段、记录模型版本、支持双索引灰度切换,比任何一次性的优化都重要。
监控。至少要盯这几项:召回率(用固定的黄金集定期跑)、p99 延迟、内存与磁盘水位、索引构建队列长度、返回结果数为零的请求占比(过滤打架的信号)、向量与业务数据的行数差(同步滞后的信号)。
十一、常见误解
“向量库就是存向量。” 存向量是最简单的部分,难点在带过滤的近邻查询、更新与删除语义、以及和数据的一致性。
“选召回率最高的索引就行。” 索引性能的差异常常被过滤条件的交互掩盖。先量清楚过滤的选择性分布,再谈索引。
“内存越大越好。” 量化加两段式重排是更划算的路线:省下的是持续的成本,付出的是可控的召回损失。
“量化一定掉点。” 半精度几乎无损,int8 的损失通常很小。只有二值量化和激进的乘积量化需要认真评估,并且用重排补偿。
“托管一定贵,自建一定便宜。” 小规模下托管的账单通常低于自建的人力与机器;规模上去之后才反过来。分界线大致在”是否需要专人运维”这个判断上。
“向量库能顺带做全文检索。” 多数系统的全文能力是后来补的,和成熟的搜索引擎不在一个水平。混合检索是刚需时,选型的天平应该偏向搜索出身的系统。
参考
- pgvector — HNSW 与 IVFFlat 索引、向量类型与维度上限、迭代式索引扫描(README 中的过滤算术)
- pgvectorscale — 基于 DiskANN 思路的 Postgres 向量索引扩展
- Qdrant — 过滤感知检索、量化、稀疏与多向量支持
- Milvus — 分布式架构、索引类型清单与 GPU 索引
- Weaviate — 混合检索、多租户与向量压缩
- Vespa — 排序表达式与向量检索的结合
- Elasticsearch / OpenSearch — dense_vector 与混合检索,注意两者的许可差异
- Redis — 向量检索能力与各版本的许可差异(LICENSE 中列明)
- LanceDB — 嵌入式列式向量库
- Chroma — 轻量嵌入式向量库
- Typesense — GPL-3.0 的检索引擎,README 中有许可选择的解释
- Meilisearch — MIT,全文加语义的混合检索
- ClickHouse 向量相似度索引文档 — 基于 usearch 的 HNSW 索引
- Apache Doris 向量检索文档 — 4.0 起的 ANN 索引、pre-filter 语义、量化对比与压测数据
- DuckDB — 嵌入式分析数据库,向量检索由 VSS 扩展提供
- sqlite-vec — SQLite 的向量检索扩展
- FAISS / hnswlib / DiskANN / cuVS / USearch — 索引库而非数据库
- Annoy — 早期的内存型索引,仓库更新已放缓
- Pinecone 文档 — serverless 架构与存储计算分离