Milvus_索引详解

"Milvus"

Posted by fengyun on April 10, 2026

Milvus 向量索引详解

全面解析 Milvus 支持的索引类型、原理、参数配置与选型建议


目录

  1. 为什么需要向量索引
  2. 索引类型总览
  3. FLAT - 暴力搜索
  4. IVF 系列索引
  5. HNSW - 图索引
  6. 稀疏向量索引
  7. 索引选型指南
  8. 参数调优建议
  9. 代码示例

一、为什么需要向量索引

向量搜索的本质

向量数据库的核心操作是近似最近邻搜索(ANN, Approximate Nearest Neighbor)

给定查询向量 q,从数据库中找到最相似的 k 个向量

暴力搜索 vs 索引搜索

方式 时间复杂度 1亿数据搜索时间 精确率
暴力搜索(FLAT) O(N) ~10秒 100%
索引搜索(HNSW) O(logN) ~10毫秒 >95%

索引通过牺牲少量精确率,换取数量级的速度提升

索引的核心思想

┌─────────────────────────────────────────────────────────┐
│  原始向量空间(100万向量)                                │
│                                                         │
│  暴力搜索:计算与100万个向量的距离                        │
│                                                         │
│  索引搜索:                                              │
│  1. 预处理:构建数据结构(索引)                          │
│  2. 搜索时:只访问少量候选向量(如1000个)                │
│  3. 返回:在这1000个中找最近的                           │
└─────────────────────────────────────────────────────────┘

二、索引类型总览

Milvus 支持的索引类型

索引类型 全称 适用向量类型 特点
FLAT 暴力搜索 稠密向量 精确率100%,速度慢
IVF_FLAT Inverted File + FLAT 稠密向量 平衡速度与精度
IVF_SQ8 IVF + Scalar Quantization 稠密向量 内存省75%
IVF_PQ IVF + Product Quantization 稠密向量 内存省90%+
HNSW Hierarchical Navigable Small World 稠密向量 ⭐ 速度最快,最常用
SCANN Google ScaNN 稠密向量 谷歌优化,超大规模
SPARSE_INVERTED_INDEX 稀疏倒排索引 稀疏向量 专为稀疏向量设计

选型速查表

数据规模 推荐索引 理由
< 10万 FLAT 精确,无需索引
10万 - 1000万 HNSW 速度极快,召回率高
1000万 - 1亿 HNSW / IVF_SQ8 平衡速度与内存
> 1亿 IVF_PQ / SCANN 极致压缩
稀疏向量 SPARSE_INVERTED_INDEX 专用优化

三、FLAT - 暴力搜索

原理

不构建任何索引结构,直接计算查询向量与所有向量的距离。

搜索过程:
for each vector in database:
    distance = calculate_distance(query, vector)
    keep top-k smallest distances
return top-k vectors

适用场景

  • ✅ 数据量很小(< 10,000)
  • ✅ 需要100%精确率(如金融风控)
  • ✅ 测试/调试阶段
  • ❌ 生产环境大数据量

代码示例

from pymilvus import Collection, FieldSchema, DataType

# 创建集合
fields = [
    FieldSchema(name="id", dtype=DataType.INT64, is_primary=True),
    FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=768)
]

# 创建 FLAT 索引(实际上不建索引,就是暴力搜索)
index_params = {
    "index_type": "FLAT",
    "metric_type": "L2",  # 欧氏距离
    "params": {}
}

collection.create_index(field_name="embedding", index_params=index_params)

四、IVF 系列索引

4.1 IVF_FLAT - 倒排文件索引

原理

基于空间划分思想:

┌─────────────────────────────────────────────────────────┐
│  步骤1:训练(K-Means聚类)                               │
│                                                         │
│  将向量空间划分为 nlist 个聚类(Voronoi单元)             │
│                                                         │
│      ○        ○        ○                                │
│     /|\      /|\      /|\      ← 聚类中心               │
│    ─┼──    ─┼──    ─┼──                               │
│     \|/      \|/      \|/                               │
│      ○        ○        ○                                │
│                                                         │
│  步骤2:搜索                                             │
│  1. 找到查询向量最近的 nprobe 个聚类                      │
│  2. 只在这 nprobe 个聚类中搜索                            │
│  3. 返回Top-K                                           │
└─────────────────────────────────────────────────────────┘

参数说明

参数 含义 建议值
nlist 聚类中心数量 4 × √N,如100万数据用 4000
nprobe 搜索时访问的聚类数 10-100,越大越准越慢

代码示例

# 创建索引
index_params = {
    "index_type": "IVF_FLAT",
    "metric_type": "L2",
    "params": {"nlist": 128}  # 聚类中心数
}
collection.create_index(field_name="embedding", index_params=index_params)

# 搜索参数
search_params = {
    "metric_type": "L2",
    "params": {"nprobe": 10}  # 搜索10个聚类
}
results = collection.search(
    data=[query_vector],
    anns_field="embedding",
    param=search_params,
    limit=10
)

4.2 IVF_SQ8 - 标量量化

在 IVF_FLAT 基础上,对每个向量进行标量量化压缩

原始向量:float32 × dim = 4 × dim 字节
SQ8压缩:int8 × dim = 1 × dim 字节

压缩率:75%(内存占用降为1/4)

适用:内存受限,可接受少量精度损失

index_params = {
    "index_type": "IVF_SQ8",
    "metric_type": "L2",
    "params": {"nlist": 128}
}

4.3 IVF_PQ - 乘积量化

更激进的压缩方案:

原理:
1. 将高维向量切分为 m 个子向量
2. 每个子向量用 k-means 量化到 nbits 位
3. 用码本索引代替原始值

压缩率:可达 1/16 ~ 1/32
参数 含义 建议值
m 子向量数量 dim 的约数,如 dim=128, m=16
nbits 每个子向量量化位数 8
index_params = {
    "index_type": "IVF_PQ",
    "metric_type": "L2",
    "params": {
        "nlist": 128,
        "m": 16,      # 子向量数
        "nbits": 8    # 量化位数
    }
}

五、HNSW - 图索引 ⭐ 最常用

5.1 原理

HNSW(Hierarchical Navigable Small World)构建多层导航图

┌─────────────────────────────────────────────────────────┐
│  HNSW 多层图结构                                         │
│                                                         │
│  Layer 2 (稀疏层):                                       │
│      ○───────────────○                                  │
│      │               │         长距离连接                │
│  Layer 1 (中层):     │                                  │
│      ○──────○───────○──○                                │
│     / \      |       |  \                               │
│  Layer 0 (密集层):   │   \                              │
│  ○─○─○─○─○─○─○─○─○─○─○─○─○                             │
│  ↑ 包含所有数据点                                        │
│                                                         │
│  搜索过程:                                              │
│  1. 从顶层随机点开始                                     │
│  2. 贪心找最近邻,逐层下降                                │
│  3. 在底层找到最近邻                                      │
└─────────────────────────────────────────────────────────┘

5.2 参数详解

参数 含义 建议值 影响
M 每个节点的最大邻居数 8-64 越大:图越密,搜索越快,内存越大
efConstruction 构建时的搜索范围 64-400 越大:图质量越好,构建越慢
ef 搜索时的候选集大小 ≥ top_k 越大:召回率越高,搜索越慢

5.3 参数选择建议

数据规模      M      efConstruction    ef(搜索时)
─────────────────────────────────────────────
< 100万      16         200            64
100万-1亿    32         400            128
> 1亿        64         400            256

5.4 代码示例

# 创建 HNSW 索引
index_params = {
    "index_type": "HNSW",
    "metric_type": "COSINE",  # 或 L2, IP
    "params": {
        "M": 16,              # 每个节点最大连接数
        "efConstruction": 200 # 构建搜索范围
    }
}
collection.create_index(field_name="embedding", index_params=index_params)

# 加载集合
collection.load()

# 搜索(指定 ef 参数)
search_params = {
    "metric_type": "COSINE",
    "params": {"ef": 64}     # 搜索候选集大小
}
results = collection.search(
    data=[query_vector],
    anns_field="embedding",
    param=search_params,
    limit=10
)

5.5 HNSW vs IVF 对比

维度 HNSW IVF_FLAT
搜索速度 ⭐⭐⭐ 极快 ⭐⭐ 快
召回率 ⭐⭐⭐ >95% ⭐⭐ ~90%
内存占用 ⭐ 大(2-3倍) ⭐⭐ 中等
构建时间 ⭐ 慢 ⭐⭐ 中等
增量更新 ⭐ 支持差 ⭐⭐⭐ 支持好
适用规模 百万-千万级 百万-亿级

六、稀疏向量索引

6.1 什么是稀疏向量

稠密向量:维度固定,每个维度都有值
  [0.1, 0.3, 0.0, 0.8, 0.2, ...]  # 768维,大部分非零

稀疏向量:维度极高,但大部分为0
  {1234: 0.8, 5678: 0.3, 9999: 0.5}  # 3万个token,只有3个非零

生成方式

  • SPLADE:基于BERT的稀疏编码器
  • BGE-M3:同时输出稠密+稀疏向量

6.2 SPARSE_INVERTED_INDEX

专为稀疏向量设计的倒排索引:

# 定义稀疏向量字段
fields = [
    FieldSchema(name="id", dtype=DataType.INT64, is_primary=True),
    FieldSchema(name="sparse_vec", dtype=DataType.SPARSE_FLOAT_VECTOR)
]

# 创建稀疏向量索引
index_params = {
    "index_type": "SPARSE_INVERTED_INDEX",
    "metric_type": "IP",  # 内积
    "params": {
        "drop_ratio_build": 0.2  # 构建时丢弃的小值比例
    }
}
collection.create_index(field_name="sparse_vec", index_params=index_params)

6.3 混合检索(稠密+稀疏)

# 同时定义两种向量
fields = [
    FieldSchema(name="id", dtype=DataType.INT64, is_primary=True),
    FieldSchema(name="dense_vec", dtype=DataType.FLOAT_VECTOR, dim=768),
    FieldSchema(name="sparse_vec", dtype=DataType.SPARSE_FLOAT_VECTOR)
]

# 分别创建索引
collection.create_index("dense_vec", {
    "index_type": "HNSW",
    "metric_type": "COSINE",
    "params": {"M": 16, "efConstruction": 200}
})

collection.create_index("sparse_vec", {
    "index_type": "SPARSE_INVERTED_INDEX",
    "metric_type": "IP",
    "params": {"drop_ratio_build": 0.2}
})

# 混合搜索
from pymilvus import AnnSearchRequest

dense_req = AnnSearchRequest(
    data=[dense_query],
    anns_field="dense_vec",
    param={"metric_type": "COSINE", "params": {"ef": 64}},
    limit=100
)

sparse_req = AnnSearchRequest(
    data=[sparse_query],
    anns_field="sparse_vec",
    param={"metric_type": "IP", "params": {"drop_ratio_build": 0.2}},
    limit=100
)

# RRF 融合
results = collection.hybrid_search(
    reqs=[dense_req, sparse_req],
    rerank=RRFRanker(),
    limit=10
)

七、索引选型指南

7.1 决策流程图

数据量 < 10万?
  ├─ 是 → 用 FLAT(精确)
  └─ 否 → 需要100%精确率?
           ├─ 是 → 用 FLAT
           └─ 否 → 内存充足?
                    ├─ 是 → 用 HNSW(速度最快)
                    └─ 否 → 数据量 > 1亿?
                             ├─ 是 → 用 IVF_PQ(压缩)
                             └─ 否 → 用 IVF_FLAT(平衡)

7.2 场景匹配

场景 推荐索引 理由
推荐系统(实时) HNSW 延迟低,用户体验好
图像检索 HNSW 特征向量维度高,HNSW效果好
文本语义搜索 HNSW + 稀疏向量 混合检索效果更好
日志分析 IVF_FLAT 数据量大,可接受稍高延迟
嵌入式设备 IVF_SQ8/PQ 内存受限
金融风控 FLAT 需要100%精确率

八、参数调优建议

8.1 通用调优原则

1. 召回率不够 → 增加搜索参数(nprobe/ef)
2. 速度太慢 → 减少搜索参数,或换更快的索引
3. 内存不够 → 使用量化索引(SQ8/PQ)
4. 构建太慢 → 减少 M 或 efConstruction

8.2 HNSW 调优

# 追求速度(召回率稍低)
index_params = {"M": 8, "efConstruction": 64}
search_params = {"ef": 32}

# 追求召回率(速度稍慢)
index_params = {"M": 32, "efConstruction": 400}
search_params = {"ef": 256}

# 平衡方案(推荐)
index_params = {"M": 16, "efConstruction": 200}
search_params = {"ef": 64}

8.3 IVF 调优

# nlist 计算:4 * sqrt(N)
import math
nlist = 4 * int(math.sqrt(num_vectors))

# nprobe 调优
# 召回率测试:逐步增加 nprobe,直到召回率满足要求

九、代码示例

9.1 完整示例:HNSW 索引

from pymilvus import connections, FieldSchema, CollectionSchema, DataType, Collection

# 连接 Milvus
connections.connect(host="localhost", port="19530")

# 定义字段
fields = [
    FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True),
    FieldSchema(name="text", dtype=DataType.VARCHAR, max_length=512),
    FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=768)
]

# 创建集合
schema = CollectionSchema(fields, description="文档向量库")
collection = Collection(name="documents", schema=schema)

# 插入数据
import numpy as np
num_entities = 10000
data = [
    [f"doc_{i}" for i in range(num_entities)],
    np.random.random((num_entities, 768)).tolist()
]
collection.insert(data)

# 创建 HNSW 索引
index_params = {
    "index_type": "HNSW",
    "metric_type": "COSINE",
    "params": {"M": 16, "efConstruction": 200}
}
collection.create_index(field_name="embedding", index_params=index_params)

# 加载到内存
collection.load()

# 搜索
query_vector = np.random.random(768).tolist()
search_params = {"metric_type": "COSINE", "params": {"ef": 64}}

results = collection.search(
    data=[query_vector],
    anns_field="embedding",
    param=search_params,
    limit=5,
    output_fields=["text"]
)

for hits in results:
    for hit in hits:
        print(f"ID: {hit.id}, Distance: {hit.distance}, Text: {hit.entity.text}")

# 释放资源
collection.release()
connections.disconnect()

9.2 索引管理操作

# 查看索引信息
collection.indexes

# 删除索引
collection.drop_index()

# 查看索引进度
utility.index_building_progress("documents")

# 查看索引状态
utility.load_state("documents")

附录:关键术语表

术语 英文 含义
ANN Approximate Nearest Neighbor 近似最近邻
IVF Inverted File 倒排文件
PQ Product Quantization 乘积量化
SQ Scalar Quantization 标量量化
HNSW Hierarchical Navigable Small World 分层可导航小世界
nlist - IVF聚类中心数
nprobe - IVF搜索时访问的聚类数
M - HNSW节点最大连接数
ef - HNSW搜索范围
Recall - 召回率
Latency - 延迟
Throughput - 吞吐量

文档生成时间:2026-04-10
适用 Milvus 版本:2.3+