文章摘要
本文针对向量检索中标准HNSW算法内存占用过高的问题,介绍了Milvus的HNSW_SQ、HNSW_PQ、HNSW_PRQ三种量化优化索引,通过对比测试不同方案与参数的性能,得出未启用重排的SQ8在召回率、吞吐与内存间平衡最优等结论,给出了不同场景下的索引选型与调参指南。

在向量数据库的高召回率检索场景中,HNSW算法是常用的分层图检索方案,但它存在明显的内存占用问题:标准HNSW使用FP32全精度向量参与建图和距离计算,同时需要维护多层级的图结构,随着数据规模扩大,索引和运行时内存消耗会显著增长。

为解决内存问题,向量数据库提供了基于HNSW的量化索引方案,包括HNSW_SQ、HNSW_PQ和HNSW_PRQ三类。这三类索引与标准HNSW共享同一套分层图构建与搜索框架,但通过不同精度的向量表示来降低内存占用;同时还支持通过更高精度的向量对候选结果进行重排,以弥补量化带来的召回率损失。

核心实验结论

  • 以HNSW FP32作为全精度基准后,应优先从SQ8(未启用refinement)开始评估量化压缩收益,在本次测试数据集下,该配置在召回率、查询吞吐和资源占用之间的平衡表现最优。
  • 启用refine_type=FP32的重排机制可以显著恢复召回率,但此时运行时内存占用可能接近甚至超过标准HNSW FP32的水平。
  • ef参数主要用于解决图搜索宽度不足导致的候选遗漏问题,refine_k则主要用于修正量化距离计算带来的候选排序偏差,二者无法互相替代。

HNSW系列索引的核心构成

理解HNSW系列索引的关键,在于区分三个核心概念:分层图搜索框架、向量量化表示和候选结果重排机制。

  • HNSW框架:负责在多层级的小世界图上进行导航搜索,从高层入口节点逐步向下遍历,快速定位目标向量所在的区域。
  • 向量量化:决定向量如何被压缩表示以及如何计算近似距离,不同的量化方式会带来不同的内存占用和召回率表现。
  • 候选重排:使用更高精度的向量表示对初步召回的候选结果重新计算距离并排序,用于弥补量化带来的精度损失。

标准HNSW直接使用FP32全精度向量,作为本次测试的全精度参考基准。HNSW_SQ采用标量量化,HNSW_PQ将向量划分为多个子向量并编码为码本索引,HNSW_PRQ则在PQ粗量化的基础上对残差进行二次迭代量化。三类量化索引均支持候选重排机制,更高精度的重排通常能提升召回率,但也会增加计算、存储和内存开销。

核心参数与量化原理

HNSW的核心参数包括M、efConstruction和ef:M控制每个节点的最大连接数,增大M可改善图连通性和召回率,但会增加内存和计算开销;efConstruction控制建图阶段的候选宽度,值越大建图质量越高但耗时越长;ef控制查询阶段的候选宽度,值越大召回率越高但延迟也会上升。

量化索引沿用相同的HNSW分层建图与搜索流程,但向量压缩表示会参与邻居选择和距离计算,因此不同量化索引生成的图拓扑和搜索路径并不完全一致。量化误差既可能改变建图阶段的邻接关系,也会影响查询阶段的候选排序。

三类量化方案详解

HNSW_SQ使用标量量化:SQ8和SQ6分别以8bit和6bit表示每个维度,为各维度独立估计量化范围;SQ4U从2.6.8版本开始支持,使用全局共享的均匀量化参数将每个值编码为4bit无符号整数,更适合归一化或维度分布均匀的数据,性能依赖内存带宽和SIMD能力。

HNSW_PQ使用乘积量化:将D维向量划分为m个子向量,每个子向量用nbits位的码本索引表示。本次测试的768维向量配置为m=96、nbits=8,每个子向量包含8个维度,理论编码长度为96字节/向量,远低于SQ8的768字节/向量。

HNSW_PRQ使用乘积残差量化:先通过PQ对原始向量进行粗量化,再用额外码本对PQ近似产生的残差进行迭代量化,参数nrq控制迭代次数。本次测试配置为m=96、nbits=8、nrq=2,理论编码长度约为192字节/向量,是PQ配置的两倍,可保留更多信息但会增加构建和计算成本。

候选重排机制详解

量化索引可通过refine=true参数启用候选重排:先使用基础量化表示执行HNSW搜索,再用refine_type指定的更高精度向量对扩大后的候选集重新计算距离并排序。refine_type的精度必须高于基础量化类型,可选包括SQ6、SQ8、BF16、FP16和FP32,其中仅FP32属于全精度重排。

进入重排的候选数=TopK×refine_k,本次测试固定TopK=100,当refine_k=4时,进入重排的候选数为400。系统最终仅返回排名最高的100个结果,增大refine_k可将因量化误差遗漏的真实近邻重新召回,但会增加计算量和延迟。

重排还会改变索引的存储与内存消耗,例如refine_type=FP32时,索引构建产物和运行时内存会显著增加,但实际内存驻留量还会受到内存映射、页面缓存和加载策略的影响。ef和refine_k面向不同瓶颈:ef解决搜索宽度不足,refine_k修正量化排序误差,二者不可互相替代。

实验设计与测试方法

本次实验围绕两个核心问题展开:一是不同量化表示能够带来多少内存与查询吞吐收益;二是当召回率下降时,应通过调整ef参数、启用重排机制,还是更换量化策略来恢复结果质量。实验基于Cohere 1M向量数据集,使用Milvus 2.6.17和PyMilvus 2.6.15完成,测试的核心指标包括Recall@100、峰值QPS、查询延迟、运行时内存占用和索引构建成本。

Recall@100采用与查询一致的余弦相似度度量,计算方式为:

Recall@100 = |ANN Top-100 ∩ Ground-truth Top-100| / 100
该指标对1000个查询的结果取平均值。峰值QPS取并发压测中的最高吞吐,串行查询的P50/P95/P99延迟则来自顺序执行的1000个查询。

运行时内存增量通过容器加载前后的内存差值统计,仅用于比较同一环境下不同配置的相对占用;索引构建产物增量通过统计索引构建期间新增的文件体积估算,不代表实例总磁盘占用;索引构建时间仅统计从创建索引到完成的时长,不包含数据导入环节。所有关键配置重复运行3次,QPS与延迟优先采用中位数结果。

HNSW系列索引横向对比

本次测试固定查询阶段的搜索宽度ef=256,启用重排的配置统一使用refine_k=4。在Cohere 1M数据集的测试中,SQ8(未启用refinement)在召回率、吞吐和资源占用之间的平衡表现最优:与标准HNSW FP32相比,其Recall@100仅下降约0.46个百分点,峰值QPS提升至1.29倍,加载后内存增量从3.22GB降至约1.01GB。

SQ8搭配refine_type=FP32的配置将Recall@100提升至0.9881,但加载后内存增量上升至约3.97GB,索引构建产物约为3.80GB,该配置的核心价值是恢复召回率,而非进一步降低内存占用。

SQ4U(未启用refinement)的峰值QPS达到1042.9,加载后内存增量降至608MB,但Recall@100仅为0.8861。在本次m=96、nbits=8的配置下,PQ(未启用refinement)的Recall@100仅为0.6083,说明96字节的PQ编码带来的量化误差已经成为主要瓶颈。PRQ配置使用nrq=2,理论编码长度约为PQ的两倍,与未启用重排的PQ相比,其Recall@100提升至0.7869,但索引构建时间长达5378秒(约89.6分钟)。PRQ搭配refine_type=FP32且refine_k=4时,Recall@100达到0.9869,但峰值QPS仅为420.4,峰值延迟P95为109.5ms,评估该配置需要同时考虑离线构建时间和在线延迟。

SQ系列量化方案对比

该组测试比较了SQ8、SQ6和SQ4U在未启用重排时的表现,以及搭配FP32和SQ8作为重排类型的效果。SQ6将索引构建产物增量从SQ8的874MB降至691MB,加载后内存增量从1006MB降至828MB,但Recall@100从0.9761降至0.9563,属于明确的空间-精度取舍。SQ4U的加载后内存增量仅为608MB,峰值QPS达1042.9,但Recall@100仅为0.8861,需要结合重排或下游排序模型才能满足业务需求。

SQ搭配FP32重排的召回恢复能力

固定ef=256时,测试不同refine_k的表现:SQ4U搭配FP32重排在refine_k=4时Recall@100达0.9830,refine_k=8时达0.9899,但加载后内存增量回升至约3.56GB,原有内存优势大幅减弱。SQ6和SQ8搭配FP32重排的结果非常接近:refine_k=4时Recall@100分别为0.9875和0.9881,refine_k=8时分别为0.9936和0.9940,此时基础量化位宽对最终召回率的影响已经很小,差异主要体现在构建成本和吞吐表现上。随着refine_k增大,峰值QPS整体下降,refine_k并非越大越好,本次测试中refine_k=4已能将多数SQ配置的召回率提升至0.983~0.988区间。

SQ搭配不同重排类型的对比

固定ef=256和refine_k=4时,使用SQ8作为重排类型的召回率低于FP32重排,但重排数据的资源占用显著下降。以SQ6为例,refine_type=SQ8时Recall@100为0.9818,加载后内存增量约为1.55GB,而FP32重排对应的内存增量约为3.76GB。因此重排机制需要联合调整refine_type和refine_k,才能平衡召回率与资源占用。

核心参数调优分析

ef参数的作用边界

ef参数控制查询阶段底层保留和评估的候选宽度,值越大召回率通常越高,但查询延迟和CPU开销也会上升。测试发现,标准HNSW FP32和SQ8(未启用refinement)的召回率会随ef增大持续上升,说明扩大搜索范围仍然有效。但对于PQ和PRQ(未启用refinement),当ef从128提升至512时,Recall@100几乎没有变化,说明此时的瓶颈不再是搜索宽度,而是量化表示的距离排序误差。当PQ/PRQ的召回率进入平台期后,继续提高ef通常只会带来有限增益,同时降低吞吐,更有效的方式是提高编码预算或启用重排机制。

refine_k参数的影响

refine_k控制重排候选集相对于TopK的放大倍数,进入重排的候选数=TopK×refine_k,本次测试固定TopK=100。测试显示,refine_k的收益与基础量化误差密切相关:SQ8的量化误差较小,refine_k=1和2时Recall@100均为0.9807,提升到4和8时召回率才会进一步上升,但峰值QPS会明显下降。PQ对refine_k的响应最为明显,Recall@100从0.8629提升至0.9848,说明FP32重排能够显著修正量化距离带来的排序偏差。PRQ搭配FP32重排在refine_k=1时已达0.9704的Recall@100,提升到4和8时分别达到0.9869和0.9931,但峰值QPS从560.1降至270.9,串行延迟P95从12.3ms升至24.5ms,生产评估还需结合并发延迟和真实查询分布。

相近召回率下的性能取舍

固定ef=256时,选取Recall@100接近0.98的配置进行对比:若业务可接受约0.976的Recall@100,SQ8(未启用refinement)可提供更高的峰值QPS和更低的运行时内存;若必须达到0.98以上的召回率,量化索引通常需要启用重排,此时refine_type=FP32可能使运行时内存占用接近或超过标准HNSW FP32。当目标Recall@100约为0.98时,SQ6搭配refine_type=SQ8的内存增量明显低于FP32重排配置;若目标接近0.99,三类SQ位宽都需要FP32重排和更大的refine_k,基础量化的内存优势会被重排数据显著抵消。

实验范围与限制

  • 本次测试仅覆盖Cohere 1M、768维向量、余弦相似度和TopK=100的场景,不同向量分布、维度、相似度度量和TopK可能产生不同结果。
  • 所有索引均固定使用M=16和efConstruction=200,未针对不同向量表示调优建图参数。
  • PQ仅测试了m=96、nbits=8的配置,PRQ仅测试了m=96、nbits=8、nrq=2的配置,未对编码参数进行完整扫描。
  • SQ仅覆盖了SQ8、SQ6和SQ4U,未测试BF16和FP16类型。
  • 测试未覆盖标量过滤、混合检索、动态写入删除、压缩合并、集群部署和多租户资源竞争场景。
  • 测试在16vCPU、64GB RAM的KVM虚拟机上完成,客户端与服务端共享CPU资源,测试结果仅用于比较同一环境下的相对差异,不应作为其他硬件环境的绝对性能预期。

索引选型与调参流程

以下选型建议仅基于本次Cohere 1M数据集的测试结果,实际决策需要结合目标向量分布、TopK、过滤条件、并发模型和硬件环境重新验证。

  • 高召回率场景:优先使用HNSW FP32建立全精度基准,固定M和efConstruction参数后扫描ef值,确认未量化条件下可达到的召回率-延迟基线。量化索引配合较大的refine_k可能获得高于基准的召回率,但需要同时考虑总计算量、并发延迟和重排数据带来的资源占用。
  • 平衡性能场景:优先评估SQ8(未启用refinement),该配置在本次测试中实现了召回率、吞吐和内存占用的最优平衡。如果需要进一步降低资源占用,可以测试SQ6,但需要接受约2个百分点的召回率损失,是否采用应以业务目标召回率为依据。
  • 低召回率容忍场景:未启用重排的PQ/PRQ配置适合作为粗召回阶段,用于生成大规模候选集,后续搭配业务排序模型或更高精度的重排机制。这类配置的压缩强度更高,内存占用更低,但召回率有限,更适合离线预筛或召回兜底场景。
  • 高召回率量化场景:当PQ/PRQ的召回率受到量化误差限制时,提高ef的收益有限,此时应启用重排机制,并联合调整refine_type和refine_k。FP32重排可以获得最高的召回率,但资源占用最大;SQ6、SQ8、BF16和FP16可作为中间精度的优化选项。

标准调参流程建议:先明确业务约束(TopK、目标召回率、QPS、延迟、内存预算、构建时间窗口),再用HNSW FP32建立基准,从SQ8开始测试量化压缩,根据召回率差距选择调整ef、启用重排或更换量化类型,最后在目标环境中重新运行基准测试验证结果。

总结

HNSW、HNSW_SQ、HNSW_PQ和HNSW_PRQ共享同一套HNSW算法框架,但向量量化表示会同时影响建图、距离计算和候选排序。选型时需要分别评估图搜索参数、编码预算和重排机制三个维度。

本次测试显示,标准HNSW FP32适合作为全精度参考基准,SQ8(未启用refinement)是平衡性能的最优起点。更激进的量化配置可以进一步降低资源占用,但需要通过更高的编码预算、重排机制或下游排序模型来恢复召回率。最终的索引选型应围绕业务目标召回率、QPS、延迟、内存占用和构建时间窗口展开,并在目标硬件和真实查询分布中重新验证。

以上内容不代表本平台立场,仅供读者参考