Jean's Blog

一个专注软件测试开发技术的个人博客

0%

RAG组件--向量数据库索引

Milvus中的常用索引介绍

FLAT 索引 – 全量扫描

每个查询向量直接与数据集中的每个向量进行比较,无需任何高级预处理或数据结构化。

工作原理:全量扫描每个查询向量,直接与数据集中的每个向量进行比较

创建索引代码示例:

1
2
3
4
5
6
7
8
9
10
11
from pymilvus import MilvusClient

index_params = MilvusClient.prepare_index_params()

index_params.add_index(
field_name="your_vector_field_name",
index_type="FLAT",
index_name="vector_index",
metric_type="L2",
params={}
)
  • 特点:
    • 无预处理:数据加载时全部加载到内存,没有任何结构化处理
    • 精准度高:能达到embedding模型给出的上限,是向量检索的”天花板”
    • 适用场景:适合数据量在几万到几十万条的情况
    • 性能局限:百万/千万级数据时延迟过高,不推荐使用

IVF_FLAT - 倒排文件

当对数据集进行聚类可以减少搜索空间,并且有足够的内存来存储聚类数据时,快速查询响应并保证高精度。

image-20250820095803094

  • 核心思想:通过聚类减少搜索空间,相似向量聚为一类(类似:一张照片中,将人、猫、狗分别进行分区)
  • 工作流程:
    • 使用k-means算法将向量聚类(nlist指定聚类数量)
    • 查询时计算与各聚类中心的距离
    • 只在最近的nprobe个聚类中进行搜索
    • 返回最相似的结果
  • 关键参数:
    • nlist:指定聚类分区数量(如64/80)
    • nprobe:搜索时考虑的聚类数量(如3/5)
  • 优势:
    • 效率提升:相比FLAT可快nlist倍(如分成80类快80倍)
    • 灵活性:通过调整nprobe平衡精度与性能
  • 局限性:精度略低于FLAT,但通过增加nprobe可接近

创建索引代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
from pymilvus import MilvusClient

index_params = MilvusClient.prepare_index_params()

index_params.add_index(
field_name="vector",
metric_type="L2",
index_type="IVF_FLAT",
index_name="vector_index",
params={
"nlist": 64 # 设置聚类数量
}
)

IVF_PQ:倒排文件与乘积量化

Inverted File with Product Quantization,是一种结合索引和压缩的混合方法。

image-20250820101017610

  • 核心原理: 结合倒排索引和乘积量化的混合方法,通过维度分解和子空间量化实现高效压缩检索
  • 工作流程:
    • 维度分解: 将高维向量分解为m个等长子向量,m值控制分解粒度和压缩率
    • 码本生成: 每个子空间用K-means聚类生成$2^n$位质心码本(如nbits=8时码本含256个质心)
    • 矢量量化: 子向量通过最近邻搜索匹配对应子空间的质心
    • 压缩表示: 最终编码由m个子空间索引组成,存储需求从$D×32$位降至$m×nbits$位
  • 量化本质:
    • 压缩原理: 通过降低参数精度(如64位浮点→8位整型)减少存储空间
    • 性能影响: 显著提升检索速度但会损失精度,适用于对速度敏感、精度要求宽松的场景
  • 关键参数:
    • k: 每个子空间的质心数,计算公式$k=2^{nbits}$
    • m: 原始向量分割的子向量数量,需满足$dim/m≥2$
    • nbits: 质心索引编码位数(典型值为8位)

创建索引代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pymilvus import MilvusClient

index_params = MilvusClient.prepare_index_params()

index_params.add_index(
field_name="vector",
metric_type="L2",
index_type="IVF_PQ",
index_name="vector_index",
params={
"nlist": 64, # 聚类中心数量,通常设置为 4*sqrt(n),n 为向量数量
"m": 32, # 向量被分割的子向量数量,通常为 dim/m >= 2,这里 128/32=4
"nbits": 8 # 每个子向量的编码位数,通常为 8 位
}
)
  • 参数配置:
    • nlist=64: 聚类中心数量,建议设为$4×\sqrt{n}$(n为向量总数)
    • m=32: 128维向量分割为32个子向量(满足128/32=4)
    • nbits=8: 每个子向量8位编码,产生256个质心($2^8$)
  • 实现特点:
    • 小数据集难以体现性能优势,适合大规模高维向量场景
    • 相比原始存储(如1536维32位浮点向量),压缩后仅需$m×nbits$存储空间

HNSW:分层可导航小世界

image-20250820101448765

分层可导航小世界 (HNSW) 算法构建了一个多层图,有点像具有不同缩放级别的地图。底层包含所有数据点,而上层则由从下层采样的数据点子集组成。

在这个层次结构中,每一层都包含代表数据点的节点,这些节点通过指示其接近度的边连接起来。较高的层级提供长距离跳跃,以快速接近目标,而较低的层级则支持细粒度搜索,以获得最准确的结果。

  1. 入口点:搜索从顶层的固定入口点开始,该入口点是图中预先确定的节点。
  2. 贪婪搜索:该算法贪婪地移动到当前层的最近邻,直到无法再接近查询向量。上层起到导航的作用,充当粗略的过滤器,为下层更精细的搜索找到潜在的入口点。
  3. 层下降:一旦在当前层达到局部最小值,算法就会使用预先建立的连接跳转到较低层,并重复贪婪搜索。
  4. 最终细化:该过程持续直至到达底层,其中最终细化步骤识别最近的邻居。

总结:

  • 算法架构: 多层图结构,类似多分辨率地图
    • 底层: 包含全部数据点
    • 上层: 下层数据点的抽样子集,层级越高数据点越稀疏
  • 搜索机制:
    • 入口点: 从顶层固定入口点启动搜索
    • 贪婪搜索: 逐层寻找最近邻,上层实现快速粗筛,下层进行精细搜索
    • 层间跳转: 到达局部最优后降层继续搜索,直至底层完成最终匹配
  • 核心参数:
    • M: 节点最大连接数(控制图密度)
    • efConstruction: 构建时的候选邻居数(影响索引质量)
  • 性能特点:
    • 精度与效率平衡良好,适合需要高准确率的场景
    • 通过层级结构实现类似雷达的渐进式搜索,逐步扩大搜索范围

创建索引代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pymilvus import MilvusClient

index_params = MilvusClient.prepare_index_params()

index_params.add_index(
field_name="vector",
metric_type="L2",
index_type="HNSW",
index_name="vector_index",
params={
"M": 64, # 最大邻居数
"efConstruction": 100 # 构建时的候选邻居数
}
)

DISKANN(磁盘版图索引)

基于 Vamana 图(类似 HNSW),将图结构和压缩向量存放在 SSD 上;在内存中只保留元数据。

查询流程:结合 PQ 压缩和图遍历,从磁盘 I/O 中读入必要节点。

优缺点

  • ✔ 适合海量数据:可支撑十亿级向量
  • ✔ RAM 占用低:仅元数据在内存
  • ✘ 延迟受 SSD IOPS 限制:对 I/O 性能依赖高

适用场景

  • 数据量远超可用内存
  • 对响应时延有一定容忍(或 SSD IOPS 足够高)

总结:

  • 核心思想:基于Vamana图(类似HNSW)结构,将图结构和压缩向量存放在SSD上,内存中仅保留元数据
  • 查询机制:结合PQ压缩和图遍历技术,按需从磁盘I/O读取必要节点
  • 优势特点:
    • 海量支持:可处理十亿级向量数据
    • 内存优化:仅需存储元数据,RAM占用极低
  • 性能局限:
    • I/O依赖:查询延迟受SSD IOPS限制
    • 速度折衷:相比纯内存方案存在性能差距
  • 适用场景:
    • 数据规模远超可用内存容量
    • 对响应时延有一定容忍度(或具备高性能SSD)

创建索引代码示例:

1
2
3
4
5
6
7
8
9
10
from pymilvus import MilvusClient

index_params = MilvusClient.prepare_index_params()

index_params.add_index(
field_name="vector",
metric_type="L2", # 支持 L2、IP 或 COSINE
index_type="DISKANN", # 使用 DiskANN 索引
index_name="vector_index"
)
  • 索引创建步骤:
    • 准备索引参数:使用prepare_index_params()方法初始化索引参数对象
    • 添加索引配置:通过add_index()方法设置字段名、度量类型和索引类型
      • 支持三种度量类型:$L2$距离、内积(、$IP$)和余弦相似度($COSINE$)
      • 索引类型需明确指定为”DISKANN”
    • 执行创建操作:调用create_index()方法并传入集合名称和索引参数
  • DiskANN特点:
    • 硬盘存储:索引数据直接存储在硬盘而非内存,适合大规模数据集
    • 简单配置:只需在索引类型参数中指定”DISKANN”即可启用
    • 自动运行:设置完成后系统会自动在硬盘上构建和运行索引
  • 注意事项:
    • 索引创建是同步操作,建议设置sync=True等待完成
    • 创建前需确保集合已存在且包含向量数据
    • 不同度量类型会影响搜索结果,需根据应用场景选择

索引和向量类型以及度量标准相关

image-20250820102716154

  • 索引体系:
    • 浮点向量:支持FLAT/IVF_FLAT/IVF_SQ8/IVF_PQ/HNSW/DISKANN等
    • 稀疏向量:仅支持SPARSE_INVERTED_INDEX
    • 二进制向量:提供BIN_FLAT/BIN_IVF_FLAT专用索引
  • 度量标准:
    • 浮点向量:适用$L_2$/IP/COSINE三种距离
    • 稀疏向量:限定IP/BM25相似度
    • 二进制向量:专用JACCARD/HAMMING距离
  • 组合规律:
    • 浮点向量索引体系最丰富(含GPU加速版本)
    • 二进制向量索引采用”BIN_”前缀标识
    • 稀疏向量仅支持倒排索引结构

各种类型索引的组合

名称 分类 适用场景 嵌入类型
FLAT 普通索引 数据集规模相对较小需要100%的召回率 浮点嵌入
IVF_FLAT 基于树的索引 查询速度要求高同时需要尽可能高的召回率
查询速度非常快内存资源有限
浮点嵌入
IVF_SQ8 基于量化的索引 可接受在召回率上有轻微折中 浮点嵌入
查询速度较快内存资源有限
IVF_PQ 基于量化的索引 可接受在召回率上有轻微折中
查询速度非常快对召回率要求高浮点嵌入
SCANN 基于量化的索引 查询速度非常快对召回率要求高内存资源充足 浮点嵌入
HNSW 基于图的索引 查询速度非常快对召回率要求高
内存资源较为充足
浮点嵌入
HNSW_SQ 基于图的索引 查询速度非常快内存资源有限
可接受在召回率上有轻微折中
浮点嵌入
HNSW_PQ 基于图的索引 查询速度中等内存资源非常有限
可接受在召回率上有轻微折中
浮点嵌入
HNSW_PRQ 基于图的索引 查询速度中等内存资源非常有限
可接受在召回率上有轻微折中
浮点嵌入
BIN_FLAT 普通索引 数据集规模较小
需要精确的搜索结果无须压缩
二进制嵌入
BIN_IVF_FLAT 基于树的索引 需要高查询速度对召回率要求高
数据集规模较大
二进制嵌入
SPARSE_INVERTED_INDEX 普通索引 数据集规模较小需要100%的召回率
适用于稀疏向量的检索
稀疏嵌入
  • FLAT索引:
    • 分类:普通索引
    • 适用场景:数据集规模较小且需要100%召回率
    • 嵌入类型:浮点嵌入
    • 特点:全量精度最高,适合对召回率要求严格的场景
  • IVF_FLAT索引:
    • 分类:基于树的索引
    • 适用场景:查询速度要求高同时需要尽可能高的召回率
    • 嵌入类型:浮点嵌入
    • 特点:通过聚类(树状结构)加速查询,适合大规模数据
  • 量化索引系列:
    • 包含IVF_SQ8/IVF_PQ/SCANN等
    • 共同特点:通过有损压缩减少内存占用
    • 取舍:在查询速度和内存占用上有优势,但会牺牲部分召回率
    • 典型应用:IVF_SQ8适合内存紧张场景,SCANN适合资源充足但对召回率要求高的场景
  • 图索引系列:
    • 包含HNSW/HNSW_SQ/HNSW_PQ等
    • 共同特点:基于图网络结构实现快速导航
    • 优势:HNSW在速度和召回率上表现优异,适合资源充足场景
    • 变体:带量化的版本(HNSW_SQ等)在内存占用和性能间取得平衡
  • 二进制索引:
    • BIN_FLAT:适合小规模二进制数据精确搜索
    • BIN_IVF_FLAT:适合大规模二进制数据高速查询
    • 特点:专门处理二进制嵌入,不进行压缩
  • 稀疏索引:
    • SPARSE_INVERTED_INDEX:专为稀疏向量设计
    • 特点:保持100%召回率,适合小规模稀疏数据

索引选型速览

  • 精度优先:
    • 选择FLAT索引
    • 特点:保证100%召回率,适合对精度要求极高的场景
  • 低延迟场景:
    • 选择HNSW系列
    • 适用条件:top-K较小且内存资源充足
    • 优势:查询速度非常快
  • 海量数据处理:
    • 选择IVF_FLAT
    • 特点:适合大规模数据和大top-K场景
    • 原理:通过聚类减少搜索范围
  • 内存优化方案:
    • IVF_SQ8或IVF_PQ
    • 特点:量化压缩减少内存占用
    • 取舍:会轻微影响召回率
  • GPU加速:
    • GPU_IVF_FLAT/GPU_IVF_PQ
    • 优势:利用GPU并行计算能力加速
  • 超大数据集:
    • DISKANN
    • 特点:数据量超过内存容量时使用
    • 原理:基于磁盘的索引结构

Milvus中索引的工作机制

image-20250820103439677

Milvus 的向量索引采用“粗—快—准”三层分层架构,以在检索效率和精度之间取得最佳平衡:

  1. 数据结构(粗过滤)— 例如 IVF 将向量按质心划分存储桶,或 HNSW构建分层图网络,通过只扫描与查询向量质心或邻域相关的子集,实现对大规模数据的快速过滤。
  2. 量化(加速计算)— 通过 SQ8、PQ 等有损压缩,将向量从 32 位浮点压缩到 8 位或更低维度,大幅减少内存和计算开销,快速计算候选集距离。
  3. 精炼器(精准重排)— 对量化后的候选结果(topK×扩展率)使用FP32 精度重新计算距离,对排序进行微调,弥补量化带来的误差,确保最终返回的 topK 结果高质量、低延迟。

总结:

  • 三层架构:
    • 数据结构层:负责粗过滤,如IVF的聚类或HNSW的图结构
    • 量化层:可选,通过SQ8/PQ等算法压缩数据
    • 精炼器:对候选结果进行精准重排
  • 数据结构层:
    • 核心功能:组织数据以加速检索
    • 实现方式:聚类(IVF)或图网络(HNSW)
    • 特点:只扫描相关子集,大幅提高效率
  • 量化技术:
    • 目的:减少内存和计算开销
    • 方法:将32位浮点压缩到8位或更低
    • 影响:会引入误差,但显著提升速度
  • 精炼器机制:
    • 工作流程:先获取topK×扩展率的候选集,再用FP32精度重算距离
    • 优势:弥补量化误差,确保最终结果质量
    • 特点:类似检索后处理,提升排序准确性
  • 设计理念:
    • “粗-快-准”分层架构
    • 在检索效率和精度间取得最佳平衡
    • 内部自动完成多阶段优化

性能的权衡

构建时间 vs QPS vs 召回率

  • 基于图(如 HNSW)通常能提供最高的 QPS 和低延迟,尤其适合 Top-K 较小(≤2 000)或对高召回率有需求的场景。
  • IVF 系列(IVF-PQ/SQ8 等)在 Top-K 较大(>2 000)时更高效,能够通过聚类分桶减少检索范围。
  • 在相同压缩率下,PQ 比 SQ8 召回率更高,但 SQ8 的查询速度略胜一筹。
  • 使用 DiskANN(磁盘+PQ 量化+Vamana 图)可处理远超内存容量的海量数据,但会受制于磁盘 IOPS。

容量与内存映射(mmap

  • 如果所有向量数据都能装进内存,可优先选用内存索引(HNSW、IVF+精炼)并配合 mmap 优化大文件访问。
  • 如果只有部分数据能进内存,DiskANN 是更稳的低延迟方案;IVFPQ/SQ8+mmap 则在成本和精度间提供折中。

过滤率(Filter Rate)与召回策略

  • 过滤率 < 85%:图索引效果最佳
  • 85% ≤ 过滤率 ≤ 95%:IVF 系列更合适
  • 过滤率 > 98%:暴力搜索(FLAT)可保证最高召回

Top-K 大小影响

  • 小 top-K、高召回:基于图
  • 大 top-K(占数据集 ≥ 1%):IVF 系列
  • 极高召回率(> 99%):FLAT+GPU 重算

总结:

  1. 构建时间 vs QPS vs 召回率
    • 基于图的索引优势:在Top-K较小($\leq 2000$)或需要高召回率时,HNSW等图结构能提供最高QPS和最低延迟。
    • IVF系列适用场景:当Top-K较大($> 2000$)时,IVF-PQ/SQ8通过聚类分桶减少检索范围更高效。
    • PQ与SQ8对比:相同压缩率下PQ召回率更高,但SQ8查询速度更快。
    • DiskANN特点:采用磁盘存储+PQ量化+Vamana图结构可处理超内存数据,但性能受磁盘IOPS限制。
  2. 容量与内存映射
    • 全内存场景:优先选择HNSW或IVF+精炼索引,配合mmap优化大文件访问。
    • 部分内存场景:
      • DiskANN是低延迟稳定方案
      • VFPQ/SQ8+mmap在成本与精度间提供折中方案
  3. 过滤率与召回策略
    • 低过滤率(<85%):图索引效果最佳
    • 中过滤率(85%-95%):IVF系列更合适
    • 极高过滤率(>98%):暴力搜索(FLAT)可保证最高召回
  4. Top-K大小影响
    • 小top-K高召回:基于图结构
    • 大top-K(≥数据集1%):IVF系列
    • 极高召回率(>99%):FLAT+GPU重算

案例:内存估算(以 1 百万条 128 维向量为例)

索引类型 组成 内存消耗
IVF-PQ 质心(2 000x128x4 B)=1 MB
簇分配(1 000 000x2 B)=2 MB
PQ(1 000 000x8 B)=8 MB
≈11 MB
IVF-PQ + 10% 精炼 IVF-PQ(11 MB)+精炼缓存(1 000 000x128 x 0.1 x4 B)=51.2 MB ≈62 MB
IVF-SQ8 质心(1 MB)+簇分配(2 MB)+SQ8(1 000 000x128 x 1 B)=128 MB ≈131 MB
IVF-FLAT 质心(1 MB)+簇分配(2 MB)+原始向量(1 000 000x128x4 B)=512 MB ≈515 MB
HNSW 图结构(1 000 000x32x4 B)=128 MB
原始向量=512 MB
≈640 MB
HNSW-PQ 图结构(128 MB)+PQ(1 000 000x8 B)=8 MB ≈136 MB
  • IVF-PQ:11MB(质心1MB + 簇分配2MB + PQ编码8MB)
  • IVF-PQ+10%精炼:62MB(基础11MB + 精炼缓存51.2MB)
  • IVF-SQ8:131MB(质心1MB + 簇分配2MB + SQ8编码128MB)
  • IVF-FLAT:515MB(含原始向量存储)
  • HNSW:<640MB(图结构128MB + 原始向量512MB)
  • HNSW-PQ:136MB(图结构128MB + PQ编码8MB)
  • 估算方法:根据实际数据量参照表格比例计算,需结合可用内存选择合适索引类型。工业项目中需查阅文档获取更精确的内存映射算法参数。