
很多团队在引入 Milvus 的时候,往往把精力放在 Embedding 模型选型、数据管道搭建、业务逻辑对接上,而忽略了最底层、也最关键的一环——向量索引选型。
但事实是:索引选型一旦错了,后面所有的优化都是在给一个错误的架构打补丁。
我见过太多这样的案例:
这些坑的共同点是:索引选型没有基于业务场景做系统性评估,而是"看哪个流行用哪个"。
所以,在讲具体索引之前,我先给出一个选型决策的核心框架:
决策维度 | 关键问题 | 影响 |
|---|---|---|
数据规模 | 数据量能全部放进内存吗? | 决定用内存索引还是磁盘索引 |
过滤比例 | 查询时标量过滤会过滤掉多少数据? | 决定用图索引还是 IVF 类索引 |
召回率要求 | 业务能接受多大的召回率损失? | 决定是否需要量化压缩 |
延迟敏感度 | P99 延迟要求多少毫秒? | 决定索引参数调优方向 |
Top-K 大小 | 每次查询返回多少条结果? | 决定 IVF 还是图索引更合适 |
把这五个问题想清楚,索引选型基本就有了方向。下面逐一展开。
Milvus 目前的索引体系已经非常成熟,官方文档将索引拆解为三个核心组件:数据结构(Data Structure)、量化压缩(Quantization)、精排修正(Refiner)。理解这三层架构,是做好选型的基础。
Milvus 支持的向量索引数据结构,本质上就两大类:
IVF(倒排文件):通过 K-Means 聚类把向量空间划分为 nlist 个簇,查询时只搜索距离查询向量最近的 nprobe 个簇。它的核心优势是内存效率高、对过滤查询友好、建索引快。
图结构(HNSW / DiskANN):基于分层可导航小世界图,查询时从上层粗粒度图逐层导航到下层细粒度图,实现对数级复杂度的搜索。核心优势是查询速度极快、召回率极高。
对比维度 | IVF 类 | HNSW | DiskANN |
|---|---|---|---|
算法原理 | 聚类分桶 | 多层图导航 | Vamana 图 + PQ |
内存占用 | 较低 | 较高(图结构开销大) | 低(图存磁盘) |
建索引速度 | 快 | 慢 | 中等 |
查询速度(无过滤) | 快,取决于 nprobe | 极快,对数复杂度 | 快,但有磁盘 I/O 开销 |
查询速度(高过滤率 >90%) | 稳定,簇级粗筛 | 不稳定,图碎片化严重 | 相对稳定 |
召回率 | 95%+(无量化)/ 70-90%(PQ) | 98%+ | 95%+ |
适用场景 | 大规模 + 有过滤 + 内存有限 | 中小规模 + 无过滤 + 追求极致召回 | 超大规模 + 内存放不下 + SSD 够快 |
量化压缩是大规模向量检索中不可或缺的一环。Milvus 目前支持三种主要量化方式:
SQ8(标量量化):将每个维度从 float32(4 字节)压缩到 8-bit(1 字节),压缩比 4:1。几何结构保持较好,召回率损失小。适合对召回率要求较高但又想省内存的场景。
PQ(乘积量化):将高维向量切分为多个子空间,每个子空间独立编码。压缩比可以达到 8:1 甚至 64:1。内存省得最多,但召回率损失也最大,通常需要配合 Refiner 使用。
RaBitQ:这是较新加入的一种量化方式,在保持高压缩率的同时,通过随机二值化技术实现了更好的召回率表现。火山引擎的测试数据显示,集成 RaBitQ 后磁盘索引 QPS 可达社区版 5 倍以上。
以 100 万条 128 维 float32 向量为例,不同量化方式的内存占用对比:
索引配置 | 向量存储 | 图/簇开销 | 总内存估算 |
|---|---|---|---|
IVF_FLAT(无压缩) | 512 MB | 3 MB | ~515 MB |
IVF_SQ8 | 128 MB | 3 MB | ~131 MB |
IVF_PQ(8 子量化器) | 8 MB | 3 MB | ~11 MB |
HNSW(无压缩) | 512 MB | 128 MB | ~640 MB |
HNSW_PQ(8 子量化器) | 8 MB | 128 MB | ~136 MB |
HNSW_SQ | 128 MB | 128 MB | ~256 MB |
这张表很直观地说明了一个问题:HNSW 的内存开销不仅仅是向量本身,图结构的开销(每个节点 32 条边 × 4 字节 × 100 万 = 128MB)往往被忽视。 在做容量规划时,必须把这部分算进去。
Refiner 是 Milvus 索引架构中一个非常巧妙的设计。它的原理是:量化压缩必然带来精度损失,所以在查询时,系统会多召回 topK × expansion_rate 个候选向量,然后用更高精度(通常是原始 float32 向量)对这些候选重新计算距离,最后返回精排后的 topK 结果。
这意味着:你可以放心地使用 PQ 把内存压到极低,然后通过 Refiner 把召回率拉回来。 代价是查询时会多一些计算开销,但对于大多数场景来说,这个 trade-off 非常值得。
理论讲完了,下面进入实战环节。我根据自己做过的和见过的项目,总结了几种典型业务场景的索引选型策略。
典型特征:数据量百万到千万级,通常有租户隔离(标量过滤),过滤率中等(30%-70%),Top-K 较小(5-20),对召回率要求高(直接影响生成质量)。
推荐索引:IVF_SQ8 + Refiner 或 HNSW_SQ
理由:RAG 场景的过滤通常是"只搜某个租户的文档",过滤率一般不会超过 85%,HNSW 在这个过滤率下表现尚可。但如果租户数量多、单个租户数据占比小,过滤率可能超过 85%,这时候 IVF 类索引更稳定。
SQ8 量化 + Refiner 的组合,能在内存占用和召回率之间取得很好的平衡。实测 128 维向量,1000 万数据,IVF_SQ8 + Refiner 的内存大约 1.5GB,Recall@10 可以稳定在 92% 以上。
参数建议:
典型特征:数据量千万到亿级,通常无标量过滤或过滤率极低,Top-K 较小(10-50),对延迟极其敏感(用户等着看结果),召回率要求高。
推荐索引:HNSW(数据能放内存)或 DiskANN(数据放不进内存)
理由:以图搜图是典型的"纯向量检索 + 低延迟 + 高召回"场景,没有过滤的干扰,HNSW 的图结构优势可以充分发挥。实测 1000 万 512 维向量,HNSW 的 P99 延迟可以控制在 30ms 以内,Recall@10 达到 97% 以上。
但如果数据量到了亿级,512 维向量的 HNSW 索引内存会超过 1TB,这时候 DiskANN 是更现实的选择。DiskANN 把图存在 SSD 上,内存只需要放 PQ 编码后的向量和部分图结构,内存占用可以降到原来的 1/10。
关键提醒:DiskANN 对 SSD 的 IOPS 要求很高。如果用普通 SATA SSD,随机读 IOPS 只有几万,查询延迟会波动很大。建议至少用企业级 NVMe SSD,随机读 IOPS 要能到 50 万以上。
典型特征:数据量大(亿级),查询并发高,Top-K 较大(100-1000),对延迟敏感但对召回率的容忍度相对较高(后面还有精排模型)。
推荐索引:IVF_PQ 或 SCANN
理由:推荐系统的向量召回阶段,本质上是在"用粗筛换速度"。IVF_PQ 的高压缩比可以让数据在有限内存中放下更大的候选池,而召回率的损失可以由后续的精排模型来弥补。
SCANN(Scalable Nearest Neighbors)是 Google 提出的各向异性量化索引,在推荐场景下表现优异,Milvus 也已经原生支持。它的特点是:在保持高吞吐的同时,对热门向量的召回率更好——这恰好符合推荐系统"头部内容要召回准、长尾内容可以容忍误差"的特点。
典型特征:同时有文本 Embedding、图像 Embedding、甚至音频 Embedding,需要做多路召回 + 融合排序,通常伴随复杂的标量过滤。
推荐索引:多 Collection 分别建索引 + Hybrid Search
Milvus 2.x 支持 Multi-Vector 和 Hybrid Search,可以为不同的模态分别建立 Collection,各自选择最合适的索引,然后在查询时通过 Reciprocal Rank Fusion (RRF) 或加权融合来合并结果。
模态 | 推荐索引 | 理由 |
|---|---|---|
文本 Embedding(768/1024 维) | IVF_SQ8 + Refiner | 维度高、数据量大、通常有过滤 |
图像 Embedding(512 维) | HNSW | 维度适中、追求高召回 |
稀疏向量(BM25/SPLADE) | SPARSE_INVERTED_INDEX | 稀疏向量专用索引 |
索引选型解决了"方向"问题,但真正决定线上表现的是参数调优和架构配置。这部分我按调优的优先级从高到低来讲。
这是影响查询延迟和召回率最直接的参数。
IVF 类索引的 nprobe:nprobe 决定了查询时搜索多少个簇。nprobe 越大,召回率越高,但延迟线性增长。实战经验是:从 nprobe = 1 开始测,逐步增加,直到 Recall 达到业务要求的阈值。 大多数场景下,nprobe 在 8-32 之间就能达到 90%+ 的召回率。
HNSW 的 ef_search:ef_search 控制搜索时的候选队列大小。和 nprobe 类似,ef_search 越大召回率越高,但延迟也越大。建议从 ef_search = 64 开始,逐步调整到 128、256,观察 Recall 和延迟的变化曲线。
调优方法论:不要靠猜,要建一个评测集。准备 1000-10000 个查询向量,用暴力搜索的结果作为 Ground Truth,计算不同参数下的 Recall@K。然后画出 Recall-Latency 曲线,找到曲线的"拐点"——那就是最优参数。
Milvus 的数据组织方式直接影响查询性能。数据被分成多个 Segment,每个 Segment 独立建索引。Segment 的大小和分布会影响查询的并行度和效率。
关键参数:
dataCoord.segment.maxSize:控制 Segment 最大大小,默认 512MB。数据量大时可以适当调大到 1024MB,减少 Segment 数量,降低索引构建和查询的开销。rootCoord.dmlChannels:控制写入通道数,影响数据写入的并行度。高并发写入场景可以适当增加。分片策略:Milvus 的 Collection 可以设置 shard 数量,shard 决定了数据写入和查询的并行度。一般来说,shard 数量和 QueryNode 的数量保持一致或成倍数关系,可以最大化查询并行度。
Milvus 支持 mmap(内存映射文件)机制,允许将部分数据保留在磁盘上,按需加载到内存。这对于数据量大于可用内存的场景非常有用。
使用建议:
Milvus 还支持 lazy load(懒加载),即 Collection 的数据在第一次被查询时才加载到内存。这在多租户场景下非常有用——不需要把所有租户的数据都预加载,而是按需加载。
QueryNode 是 Milvus 中负责执行查询的节点,它的资源配置直接影响查询性能。
CPU:查询是 CPU 密集型操作,尤其是距离计算部分。建议 QueryNode 的 CPU 核数不少于 8 核,高并发场景建议 16 核以上。
内存:必须保证所有需要查询的索引和数据都能加载到内存中(或使用 mmap)。内存不足会导致 OOM 或频繁的 swap,直接导致查询超时。
GPU(可选):如果使用了 GPU_IVF_FLAT 或 GPU_CAGRA 索引,GPU 的显存和计算能力会成为关键瓶颈。GPU 索引在大规模批量查询场景下优势明显,单条查询的优势不大。
一个常被忽视的调优点是写入和查询的资源隔离。Milvus 的写入流程(DataNode → Segment flush → IndexNode 建索引 → QueryNode 加载)会消耗 CPU 和 I/O 资源,如果在查询高峰期进行大量写入或索引构建,会直接影响查询延迟。
实战建议:
最后这部分,我分享几个在生产环境中踩过的真实坑,希望能帮你避坑。
这是最常见的线上故障之一。系统上线初期没有标量过滤,HNSW 跑得很好。后来业务加了过滤条件(比如"只搜最近 7 天的数据"),过滤率突然到了 90% 以上,HNSW 的图结构被大量"屏蔽"节点破坏,搜索退化成接近暴力遍历。
解决方案:如果你的业务过滤率可能超过 85%,从一开始就不要用 HNSW,改用 IVF 类索引。IVF 的簇级粗筛天然适合和标量过滤配合——先通过过滤条件排除不需要的簇,再在剩余簇中做向量检索,性能稳定可预期。
很多团队在做容量规划时只算了数据存储的内存,忽略了索引构建过程中的额外内存开销。HNSW 建索引时需要在内存中构建完整的图结构,临时内存可能是最终索引大小的 1.5-2 倍。IVF 建索引时需要做 K-Means 聚类,也需要额外的内存来存储中间结果。
建议:建索引时预留的内存至少是最终索引大小的 2 倍。如果内存不够,可以分批导入数据,分段建索引。
高维向量确实能表达更丰富的语义信息,但维度越高,索引的内存开销越大,距离计算越慢,"维度诅咒"也会导致向量之间的距离区分度下降。
实测数据:
向量维度 | 100 万条 IVF_SQ8 内存 | P99 延迟(nprobe=16) | Recall@10 |
|---|---|---|---|
128 维 | ~131 MB | ~15ms | 93.2% |
256 维 | ~259 MB | ~28ms | 91.8% |
512 维 | ~515 MB | ~52ms | 89.5% |
768 维 | ~771 MB | ~78ms | 87.3% |
1024 维 | ~1027 MB | ~105ms | 85.6% |
可以看到,从 128 维到 1024 维,内存线性增长,延迟线性增长,但召回率反而在下降。这是因为维度越高,向量空间中所有点之间的距离趋于均匀,区分度下降。
建议:在选择 Embedding 模型时,不要盲目追求高维度。如果 128 维的模型在你的业务上 Recall 够用,就不要用 768 维的模型。维度的选择应该在"表达能力"和"检索效率"之间找到平衡点。
Milvus 对标量字段也支持建立索引(INVERTED、BITMAP、STL_SORT 等)。在混合查询(向量检索 + 标量过滤)场景下,标量字段的索引质量直接影响过滤效率。
建议:
最后,我把全文的核心结论浓缩成一张速查表,方便你在实际项目中快速决策:
场景 | 数据规模 | 过滤率 | 推荐索引 | 量化方式 | 关键参数 |
|---|---|---|---|---|---|
RAG 知识库 | 百万-千万 | <85% | HNSW / IVF_SQ8 | SQ8 + Refiner | ef=128 / nprobe=16 |
RAG 知识库(高过滤) | 百万-千万 |
| IVF_SQ8 | SQ8 + Refiner | nlist=2048, nprobe=16 |
以图搜图 | 千万级 | 无/极低 | HNSW | 无压缩 | M=16, ef=256 |
以图搜图(亿级) | 亿级 | 无/极低 | DiskANN | PQ + Refiner | SSD IOPS > 50万 |
推荐系统召回 | 亿级 | 中等 | IVF_PQ / SCANN | PQ | nlist=4096, nprobe=32 |
多模态混合检索 | 百万-千万 | 中等 | 多 Collection 各自建索引 | 按模态选择 | Hybrid Search + RRF |
超大规模精确检索 | 任意 |
| FLAT(暴力搜索) | 无 | 配合 GPU 加速 |
最后的最后,三句话总结:
希望这篇文章能帮到正在做向量检索系统选型的你。如果有任何疑问或者想交流实战经验,欢迎在评论区留言。
本文基于 Milvus 2.x 版本撰写,部分特性可能随版本更新有所变化。建议参考 Milvus 官方文档获取最新信息。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。