DiskANN 索引(基于磁盘的近似最近邻)
面向十亿级近似最近邻搜索的磁盘图索引,以较低 QPS 为代价,在大幅降低内存占用的同时保持较高 Recall。
一种基于磁盘的图索引,专为十亿级向量搜索设计 — 将压缩向量保存在内存中,全精度向量存储在磁盘上,实现高 Recall 的近似最近邻搜索,同时大幅降低内存占用。
DiskANN 由 Subramanya 等人在 NeurIPS 2019 论文 DiskANN: Fast Accurate Billion-point Nearest Neighbor Search on a Single Node 中提出。
不过,由于查询路径涉及磁盘 I/O,DiskANN 的 QPS 低于纯内存索引(如 HNSW),因此更适合对吞吐和延迟不敏感、但需要在受限内存下处理超大规模数据的场景。
工作原理
DiskANN 在完整数据集上构建 Vamana 图,并将其与原始向量一起存储在磁盘上。搜索时,仅 PQ(乘积量化)压缩编码驻留在内存中,图和全精度向量按需从磁盘读取。
- Vamana 图用于导航 🪜
- 单层图结构,每个节点最多连接
max_degree个邻居。 - 图的构建采用贪心搜索-剪枝策略,通过 alpha 参数鼓励长距离边,实现快速收敛到查询点的邻域。
- 中心点(medoid,距数据集质心最近的点)作为每次搜索的固定入口。
- 单层图结构,每个节点最多连接
- 乘积量化(PQ)用于距离估计 🔍
- 向量空间被划分为
pq_chunk_num个子空间,每个子向量被量化为 256 个质心的码本(8-bit PQ 编码)。 - 查询时,预先计算查询向量的 PQ 距离查找表,通过快速表查找而非全精度运算来计算候选点的近似距离。
- 向量空间被划分为
- 带缓存的束搜索 🔎
- 搜索从中心点开始,使用束搜索(beam search)策略探索图 — 多个前沿节点通过批量磁盘 I/O 并发扩展。
- 入口点附近的高频访问节点被缓存在内存中(BFS 层级缓存),减少热点区域的磁盘读取。
- 对于每个访问的节点,使用 PQ 编码计算近似距离,并从磁盘读取全精度向量为 top-k 候选结果计算精确距离。
何时使用 DiskANN 索引?
- ✅ 无法完全装入内存的十亿级数据集
- ✅ 对成本敏感、需要最小化内存消耗的部署场景
- ✅ 可以容忍相比内存索引稍高延迟的批处理工作负载和离线分析
最佳实践:当数据集远超可用内存时使用 DiskANN。它以仅与 PQ 编码成比例的内存消耗提供出色的 Recall。如果数据集可以装入内存,建议使用 HNSW 或 HNSW-RaBitQ 以获得更低延迟。
优势
- ✨ 极低的内存占用 — 仅 PQ 压缩编码(每向量每 chunk 1 字节)驻留内存,使十亿级搜索在普通硬件上成为可能
- ✨ 高 Recall — Vamana 图通过基于 alpha 的剪枝保持连通性和多样性,最终候选结果重新计算精确距离
- ✨ 可扩展的图构建 — 采用简单的贪心插入-剪枝算法,可跨线程并行化
权衡
- ⚠️ 较高的查询延迟 — 每次搜索需要磁盘 I/O 进行图遍历,比纯内存索引(如 HNSW)慢
- ⚠️ 构建时 PQ 训练开销 — 索引构建前需要基于 KMeans 的 PQ 训练步骤,增加总构建时间
- ⚠️ 不适合实时工作负载 — 磁盘访问延迟意味着 DiskANN 更适合面向可容忍延迟或 QPS 要求相对低的场景
关键参数
调参建议:
从默认值开始。先调整查询时的 list_size 来权衡 Recall 和延迟。仅在需要更好 Recall 时增加 max_degree — 但预期会增加磁盘占用和构建时间。如果需要进一步降低内存且可接受稍低 Recall,可减少 pq_chunk_num。
索引构建参数
| 参数 | 描述 | 调参指南 |
|---|---|---|
metric_type | 用于比较向量的相似度度量 | 根据 Embedding 模型的训练方式选择 |
max_degree | 每个节点的最大邻居数 — Vamana 图中每个节点的最大边数 | • 更大的 max_degree → ✨ 更好的 Recall 和图连通性 ⚠️ 更多磁盘占用和更长的构建时间 |
list_size | 构建时候选列表大小 — 插入新向量时图构建过程中考虑的候选数量 | • 更大的 list_size → ✨ 更好的图质量和更高的 Recall ⚠️ 更长的索引构建时间 |
pq_chunk_num | PQ 子空间数量 — 控制向量维度如何划分以进行乘积量化 | • 更多 chunk → ✨ 更精细的距离近似和更好的 Recall ⚠️ PQ 编码占用更多内存(每向量每 chunk 1 字节) |
索引查询参数
| 参数 | 描述 | 调参指南 |
|---|---|---|
list_size | 查询时候选列表大小 — 束搜索图遍历时维护的候选数量 | • 更大的 list_size → ✨ 更高的 Recall ⚠️ 更多磁盘 I/O 和更高的查询延迟 |