「milvus-course-ai.zip」
链接:https://pan.quark.cn/s/00f3d411bb6d
github:https://github.com/yuanmomoya/milvus
学习目标
学完本章后,你应该能够:
- 解释 FLAT、IVF、HNSW、PQ、DISKANN 五大索引家族的核心思想。
- 画出每种索引的搜索路径和数据结构。
- 根据数据规模、内存预算和延迟要求选择索引。
- 理解索引参数如何影响召回率、QPS 和内存。
- 在 Milvus 中创建和切换不同索引。
索引的本质
向量索引的目标:用可控的召回损失换取数量级的搜索加速。
#mermaid-svg-VNXldSzepTq6cxOd{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-VNXldSzepTq6cxOd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-VNXldSzepTq6cxOd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-VNXldSzepTq6cxOd .error-icon{fill:#552222;}#mermaid-svg-VNXldSzepTq6cxOd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-VNXldSzepTq6cxOd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-VNXldSzepTq6cxOd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-VNXldSzepTq6cxOd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-VNXldSzepTq6cxOd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-VNXldSzepTq6cxOd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-VNXldSzepTq6cxOd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-VNXldSzepTq6cxOd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-VNXldSzepTq6cxOd .marker.cross{stroke:#333333;}#mermaid-svg-VNXldSzepTq6cxOd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-VNXldSzepTq6cxOd p{margin:0;}#mermaid-svg-VNXldSzepTq6cxOd .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-VNXldSzepTq6cxOd .cluster-label text{fill:#333;}#mermaid-svg-VNXldSzepTq6cxOd .cluster-label span{color:#333;}#mermaid-svg-VNXldSzepTq6cxOd .cluster-label span p{background-color:transparent;}#mermaid-svg-VNXldSzepTq6cxOd .label text,#mermaid-svg-VNXldSzepTq6cxOd span{fill:#333;color:#333;}#mermaid-svg-VNXldSzepTq6cxOd .node rect,#mermaid-svg-VNXldSzepTq6cxOd .node circle,#mermaid-svg-VNXldSzepTq6cxOd .node ellipse,#mermaid-svg-VNXldSzepTq6cxOd .node polygon,#mermaid-svg-VNXldSzepTq6cxOd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-VNXldSzepTq6cxOd .rough-node .label text,#mermaid-svg-VNXldSzepTq6cxOd .node .label text,#mermaid-svg-VNXldSzepTq6cxOd .image-shape .label,#mermaid-svg-VNXldSzepTq6cxOd .icon-shape .label{text-anchor:middle;}#mermaid-svg-VNXldSzepTq6cxOd .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-VNXldSzepTq6cxOd .rough-node .label,#mermaid-svg-VNXldSzepTq6cxOd .node .label,#mermaid-svg-VNXldSzepTq6cxOd .image-shape .label,#mermaid-svg-VNXldSzepTq6cxOd .icon-shape .label{text-align:center;}#mermaid-svg-VNXldSzepTq6cxOd .node.clickable{cursor:pointer;}#mermaid-svg-VNXldSzepTq6cxOd .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-VNXldSzepTq6cxOd .arrowheadPath{fill:#333333;}#mermaid-svg-VNXldSzepTq6cxOd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-VNXldSzepTq6cxOd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-VNXldSzepTq6cxOd .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VNXldSzepTq6cxOd .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-VNXldSzepTq6cxOd .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VNXldSzepTq6cxOd .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-VNXldSzepTq6cxOd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-VNXldSzepTq6cxOd .cluster text{fill:#333;}#mermaid-svg-VNXldSzepTq6cxOd .cluster span{color:#333;}#mermaid-svg-VNXldSzepTq6cxOd div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-VNXldSzepTq6cxOd .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-VNXldSzepTq6cxOd rect.text{fill:none;stroke-width:0;}#mermaid-svg-VNXldSzepTq6cxOd .icon-shape,#mermaid-svg-VNXldSzepTq6cxOd .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VNXldSzepTq6cxOd .icon-shape p,#mermaid-svg-VNXldSzepTq6cxOd .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-VNXldSzepTq6cxOd .icon-shape .label rect,#mermaid-svg-VNXldSzepTq6cxOd .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VNXldSzepTq6cxOd .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-VNXldSzepTq6cxOd .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-VNXldSzepTq6cxOd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 暴力扫描 FLAT
ANN 索引
100 万向量
搜索方式
扫描 100 万次
延迟 ~500ms
召回 100%
扫描 ~1 万次
延迟 ~5ms
召回 95%+
所有 ANN 索引都在回答同一个问题:如何只看一小部分数据,就能大概率找到真正最近的 TopK?
不同索引用不同策略回答这个问题:
| 索引 | 策略 | 类比 |
|---|---|---|
| IVF | 先分区,只搜最近的几个区 | 先确定城市区域,再找具体地址 |
| HNSW | 建多层图,从粗到细导航 | 先走高速公路,再走小路 |
| PQ | 压缩向量,用近似距离筛选 | 先看缩略图,再看原图 |
| DISKANN | 图索引放磁盘,按需加载 | 地图太大放硬盘,翻到哪页看哪页 |
FLAT(暴力搜索)
FLAT 不是真正的索引,它保存原始向量,搜索时逐一计算距离。
#mermaid-svg-FSukWLClc8SNKzHl{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-FSukWLClc8SNKzHl .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FSukWLClc8SNKzHl .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FSukWLClc8SNKzHl .error-icon{fill:#552222;}#mermaid-svg-FSukWLClc8SNKzHl .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FSukWLClc8SNKzHl .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FSukWLClc8SNKzHl .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FSukWLClc8SNKzHl .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FSukWLClc8SNKzHl .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FSukWLClc8SNKzHl .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FSukWLClc8SNKzHl .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FSukWLClc8SNKzHl .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FSukWLClc8SNKzHl .marker.cross{stroke:#333333;}#mermaid-svg-FSukWLClc8SNKzHl svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FSukWLClc8SNKzHl p{margin:0;}#mermaid-svg-FSukWLClc8SNKzHl .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-FSukWLClc8SNKzHl .cluster-label text{fill:#333;}#mermaid-svg-FSukWLClc8SNKzHl .cluster-label span{color:#333;}#mermaid-svg-FSukWLClc8SNKzHl .cluster-label span p{background-color:transparent;}#mermaid-svg-FSukWLClc8SNKzHl .label text,#mermaid-svg-FSukWLClc8SNKzHl span{fill:#333;color:#333;}#mermaid-svg-FSukWLClc8SNKzHl .node rect,#mermaid-svg-FSukWLClc8SNKzHl .node circle,#mermaid-svg-FSukWLClc8SNKzHl .node ellipse,#mermaid-svg-FSukWLClc8SNKzHl .node polygon,#mermaid-svg-FSukWLClc8SNKzHl .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FSukWLClc8SNKzHl .rough-node .label text,#mermaid-svg-FSukWLClc8SNKzHl .node .label text,#mermaid-svg-FSukWLClc8SNKzHl .image-shape .label,#mermaid-svg-FSukWLClc8SNKzHl .icon-shape .label{text-anchor:middle;}#mermaid-svg-FSukWLClc8SNKzHl .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-FSukWLClc8SNKzHl .rough-node .label,#mermaid-svg-FSukWLClc8SNKzHl .node .label,#mermaid-svg-FSukWLClc8SNKzHl .image-shape .label,#mermaid-svg-FSukWLClc8SNKzHl .icon-shape .label{text-align:center;}#mermaid-svg-FSukWLClc8SNKzHl .node.clickable{cursor:pointer;}#mermaid-svg-FSukWLClc8SNKzHl .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-FSukWLClc8SNKzHl .arrowheadPath{fill:#333333;}#mermaid-svg-FSukWLClc8SNKzHl .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FSukWLClc8SNKzHl .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FSukWLClc8SNKzHl .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FSukWLClc8SNKzHl .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-FSukWLClc8SNKzHl .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FSukWLClc8SNKzHl .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-FSukWLClc8SNKzHl .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FSukWLClc8SNKzHl .cluster text{fill:#333;}#mermaid-svg-FSukWLClc8SNKzHl .cluster span{color:#333;}#mermaid-svg-FSukWLClc8SNKzHl div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-FSukWLClc8SNKzHl .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-FSukWLClc8SNKzHl rect.text{fill:none;stroke-width:0;}#mermaid-svg-FSukWLClc8SNKzHl .icon-shape,#mermaid-svg-FSukWLClc8SNKzHl .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FSukWLClc8SNKzHl .icon-shape p,#mermaid-svg-FSukWLClc8SNKzHl .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-FSukWLClc8SNKzHl .icon-shape .label rect,#mermaid-svg-FSukWLClc8SNKzHl .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FSukWLClc8SNKzHl .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-FSukWLClc8SNKzHl .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-FSukWLClc8SNKzHl :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 查询向量
逐一计算距离
排序取 TopK
精确结果
特性
| 维度 | 表现 |
|---|---|
| 召回率 | 100%(精确搜索) |
| 搜索速度 | O(N),随数据量线性增长 |
| 内存 | 仅原始向量,无额外开销 |
| 构建时间 | 无(不需要构建) |
| 适用规模 | < 10 万条 |
在 Milvus 中使用
python
index_params.add_index(
field_name="embedding",
index_type="FLAT",
metric_type="COSINE",
)
何时用 FLAT
- 数据量小(< 10 万),暴力扫描延迟可接受
- 需要 100% 召回率的评测基准
- 开发调试阶段,不想调索引参数
IVF(倒排文件索引)
IVF 的核心思想:先把向量空间聚成 nlist 个簇,搜索时只扫描最近的 nprobe 个簇。
构建过程
#mermaid-svg-5Uog9Tald6FctqbN{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5Uog9Tald6FctqbN .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5Uog9Tald6FctqbN .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5Uog9Tald6FctqbN .error-icon{fill:#552222;}#mermaid-svg-5Uog9Tald6FctqbN .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5Uog9Tald6FctqbN .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5Uog9Tald6FctqbN .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5Uog9Tald6FctqbN .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5Uog9Tald6FctqbN .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5Uog9Tald6FctqbN .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5Uog9Tald6FctqbN .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5Uog9Tald6FctqbN .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5Uog9Tald6FctqbN .marker.cross{stroke:#333333;}#mermaid-svg-5Uog9Tald6FctqbN svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5Uog9Tald6FctqbN p{margin:0;}#mermaid-svg-5Uog9Tald6FctqbN .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-5Uog9Tald6FctqbN .cluster-label text{fill:#333;}#mermaid-svg-5Uog9Tald6FctqbN .cluster-label span{color:#333;}#mermaid-svg-5Uog9Tald6FctqbN .cluster-label span p{background-color:transparent;}#mermaid-svg-5Uog9Tald6FctqbN .label text,#mermaid-svg-5Uog9Tald6FctqbN span{fill:#333;color:#333;}#mermaid-svg-5Uog9Tald6FctqbN .node rect,#mermaid-svg-5Uog9Tald6FctqbN .node circle,#mermaid-svg-5Uog9Tald6FctqbN .node ellipse,#mermaid-svg-5Uog9Tald6FctqbN .node polygon,#mermaid-svg-5Uog9Tald6FctqbN .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5Uog9Tald6FctqbN .rough-node .label text,#mermaid-svg-5Uog9Tald6FctqbN .node .label text,#mermaid-svg-5Uog9Tald6FctqbN .image-shape .label,#mermaid-svg-5Uog9Tald6FctqbN .icon-shape .label{text-anchor:middle;}#mermaid-svg-5Uog9Tald6FctqbN .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5Uog9Tald6FctqbN .rough-node .label,#mermaid-svg-5Uog9Tald6FctqbN .node .label,#mermaid-svg-5Uog9Tald6FctqbN .image-shape .label,#mermaid-svg-5Uog9Tald6FctqbN .icon-shape .label{text-align:center;}#mermaid-svg-5Uog9Tald6FctqbN .node.clickable{cursor:pointer;}#mermaid-svg-5Uog9Tald6FctqbN .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5Uog9Tald6FctqbN .arrowheadPath{fill:#333333;}#mermaid-svg-5Uog9Tald6FctqbN .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5Uog9Tald6FctqbN .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5Uog9Tald6FctqbN .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5Uog9Tald6FctqbN .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5Uog9Tald6FctqbN .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5Uog9Tald6FctqbN .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5Uog9Tald6FctqbN .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5Uog9Tald6FctqbN .cluster text{fill:#333;}#mermaid-svg-5Uog9Tald6FctqbN .cluster span{color:#333;}#mermaid-svg-5Uog9Tald6FctqbN div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5Uog9Tald6FctqbN .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5Uog9Tald6FctqbN rect.text{fill:none;stroke-width:0;}#mermaid-svg-5Uog9Tald6FctqbN .icon-shape,#mermaid-svg-5Uog9Tald6FctqbN .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5Uog9Tald6FctqbN .icon-shape p,#mermaid-svg-5Uog9Tald6FctqbN .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5Uog9Tald6FctqbN .icon-shape .label rect,#mermaid-svg-5Uog9Tald6FctqbN .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5Uog9Tald6FctqbN .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5Uog9Tald6FctqbN .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5Uog9Tald6FctqbN :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 全部向量
KMeans 聚类
簇 1: 中心 C1
簇 2: 中心 C2
簇 3: 中心 C3
簇 N: 中心 CN
倒排列表 1
属于簇 1 的所有向量
倒排列表 2
倒排列表 3
倒排列表 N
搜索过程
#mermaid-svg-laSycC6vizSw2LxA{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-laSycC6vizSw2LxA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-laSycC6vizSw2LxA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-laSycC6vizSw2LxA .error-icon{fill:#552222;}#mermaid-svg-laSycC6vizSw2LxA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-laSycC6vizSw2LxA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-laSycC6vizSw2LxA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-laSycC6vizSw2LxA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-laSycC6vizSw2LxA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-laSycC6vizSw2LxA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-laSycC6vizSw2LxA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-laSycC6vizSw2LxA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-laSycC6vizSw2LxA .marker.cross{stroke:#333333;}#mermaid-svg-laSycC6vizSw2LxA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-laSycC6vizSw2LxA p{margin:0;}#mermaid-svg-laSycC6vizSw2LxA .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-laSycC6vizSw2LxA .cluster-label text{fill:#333;}#mermaid-svg-laSycC6vizSw2LxA .cluster-label span{color:#333;}#mermaid-svg-laSycC6vizSw2LxA .cluster-label span p{background-color:transparent;}#mermaid-svg-laSycC6vizSw2LxA .label text,#mermaid-svg-laSycC6vizSw2LxA span{fill:#333;color:#333;}#mermaid-svg-laSycC6vizSw2LxA .node rect,#mermaid-svg-laSycC6vizSw2LxA .node circle,#mermaid-svg-laSycC6vizSw2LxA .node ellipse,#mermaid-svg-laSycC6vizSw2LxA .node polygon,#mermaid-svg-laSycC6vizSw2LxA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-laSycC6vizSw2LxA .rough-node .label text,#mermaid-svg-laSycC6vizSw2LxA .node .label text,#mermaid-svg-laSycC6vizSw2LxA .image-shape .label,#mermaid-svg-laSycC6vizSw2LxA .icon-shape .label{text-anchor:middle;}#mermaid-svg-laSycC6vizSw2LxA .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-laSycC6vizSw2LxA .rough-node .label,#mermaid-svg-laSycC6vizSw2LxA .node .label,#mermaid-svg-laSycC6vizSw2LxA .image-shape .label,#mermaid-svg-laSycC6vizSw2LxA .icon-shape .label{text-align:center;}#mermaid-svg-laSycC6vizSw2LxA .node.clickable{cursor:pointer;}#mermaid-svg-laSycC6vizSw2LxA .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-laSycC6vizSw2LxA .arrowheadPath{fill:#333333;}#mermaid-svg-laSycC6vizSw2LxA .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-laSycC6vizSw2LxA .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-laSycC6vizSw2LxA .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-laSycC6vizSw2LxA .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-laSycC6vizSw2LxA .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-laSycC6vizSw2LxA .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-laSycC6vizSw2LxA .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-laSycC6vizSw2LxA .cluster text{fill:#333;}#mermaid-svg-laSycC6vizSw2LxA .cluster span{color:#333;}#mermaid-svg-laSycC6vizSw2LxA div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-laSycC6vizSw2LxA .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-laSycC6vizSw2LxA rect.text{fill:none;stroke-width:0;}#mermaid-svg-laSycC6vizSw2LxA .icon-shape,#mermaid-svg-laSycC6vizSw2LxA .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-laSycC6vizSw2LxA .icon-shape p,#mermaid-svg-laSycC6vizSw2LxA .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-laSycC6vizSw2LxA .icon-shape .label rect,#mermaid-svg-laSycC6vizSw2LxA .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-laSycC6vizSw2LxA .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-laSycC6vizSw2LxA .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-laSycC6vizSw2LxA :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 查询向量
计算与所有中心的距离
选择最近的 nprobe 个中心
扫描这 nprobe 个列表中的向量
排序取 TopK
参数详解
| 参数 | 阶段 | 作用 | 增大的影响 |
|---|---|---|---|
nlist |
构建 | 聚类中心数量 | 每个列表更短,搜索更快,但边界效应增加 |
nprobe |
搜索 | 探测的列表数量 | 召回更高,延迟更大 |
nlist 经验值 :nlist ≈ 4 × sqrt(N),N 为向量总数。100 万条 → nlist ≈ 4000。
nprobe 与召回的关系:
| nprobe / nlist | 典型召回率 | 说明 |
|---|---|---|
| 1% | 60-70% | 太低,不建议 |
| 5% | 85-90% | 可接受 |
| 10% | 92-96% | 推荐起点 |
| 20%+ | 97%+ | 接近暴力扫描 |
IVF 变体
| 变体 | 区别 | 内存 | 精度 |
|---|---|---|---|
IVF_FLAT |
列表中存原始向量 | 高 | 高 |
IVF_SQ8 |
列表中存 8bit 量化向量 | 低 ~25% | 略低 |
IVF_PQ |
列表中存 PQ 编码 | 很低 | 较低 |
在 Milvus 中使用
python
index_params.add_index(
field_name="embedding",
index_type="IVF_FLAT",
metric_type="COSINE",
params={"nlist": 1024},
)
# 搜索时
search_params = {
"metric_type": "COSINE",
"params": {"nprobe": 64},
}
HNSW(分层可导航小世界图)
HNSW 是目前最流行的高性能索引。核心思想:建一张多层图,高层稀疏用于快速接近目标,底层密集用于精细搜索。
数据结构
#mermaid-svg-pUNPHZLclPvJxuRH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-pUNPHZLclPvJxuRH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-pUNPHZLclPvJxuRH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-pUNPHZLclPvJxuRH .error-icon{fill:#552222;}#mermaid-svg-pUNPHZLclPvJxuRH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-pUNPHZLclPvJxuRH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-pUNPHZLclPvJxuRH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-pUNPHZLclPvJxuRH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-pUNPHZLclPvJxuRH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-pUNPHZLclPvJxuRH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-pUNPHZLclPvJxuRH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-pUNPHZLclPvJxuRH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-pUNPHZLclPvJxuRH .marker.cross{stroke:#333333;}#mermaid-svg-pUNPHZLclPvJxuRH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-pUNPHZLclPvJxuRH p{margin:0;}#mermaid-svg-pUNPHZLclPvJxuRH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-pUNPHZLclPvJxuRH .cluster-label text{fill:#333;}#mermaid-svg-pUNPHZLclPvJxuRH .cluster-label span{color:#333;}#mermaid-svg-pUNPHZLclPvJxuRH .cluster-label span p{background-color:transparent;}#mermaid-svg-pUNPHZLclPvJxuRH .label text,#mermaid-svg-pUNPHZLclPvJxuRH span{fill:#333;color:#333;}#mermaid-svg-pUNPHZLclPvJxuRH .node rect,#mermaid-svg-pUNPHZLclPvJxuRH .node circle,#mermaid-svg-pUNPHZLclPvJxuRH .node ellipse,#mermaid-svg-pUNPHZLclPvJxuRH .node polygon,#mermaid-svg-pUNPHZLclPvJxuRH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-pUNPHZLclPvJxuRH .rough-node .label text,#mermaid-svg-pUNPHZLclPvJxuRH .node .label text,#mermaid-svg-pUNPHZLclPvJxuRH .image-shape .label,#mermaid-svg-pUNPHZLclPvJxuRH .icon-shape .label{text-anchor:middle;}#mermaid-svg-pUNPHZLclPvJxuRH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-pUNPHZLclPvJxuRH .rough-node .label,#mermaid-svg-pUNPHZLclPvJxuRH .node .label,#mermaid-svg-pUNPHZLclPvJxuRH .image-shape .label,#mermaid-svg-pUNPHZLclPvJxuRH .icon-shape .label{text-align:center;}#mermaid-svg-pUNPHZLclPvJxuRH .node.clickable{cursor:pointer;}#mermaid-svg-pUNPHZLclPvJxuRH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-pUNPHZLclPvJxuRH .arrowheadPath{fill:#333333;}#mermaid-svg-pUNPHZLclPvJxuRH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-pUNPHZLclPvJxuRH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-pUNPHZLclPvJxuRH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pUNPHZLclPvJxuRH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-pUNPHZLclPvJxuRH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pUNPHZLclPvJxuRH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-pUNPHZLclPvJxuRH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-pUNPHZLclPvJxuRH .cluster text{fill:#333;}#mermaid-svg-pUNPHZLclPvJxuRH .cluster span{color:#333;}#mermaid-svg-pUNPHZLclPvJxuRH div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-pUNPHZLclPvJxuRH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-pUNPHZLclPvJxuRH rect.text{fill:none;stroke-width:0;}#mermaid-svg-pUNPHZLclPvJxuRH .icon-shape,#mermaid-svg-pUNPHZLclPvJxuRH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pUNPHZLclPvJxuRH .icon-shape p,#mermaid-svg-pUNPHZLclPvJxuRH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-pUNPHZLclPvJxuRH .icon-shape .label rect,#mermaid-svg-pUNPHZLclPvJxuRH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pUNPHZLclPvJxuRH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-pUNPHZLclPvJxuRH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-pUNPHZLclPvJxuRH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Layer 0 - 密集邻接图(所有节点)
A
B
C
D
E
F
G
Layer 1 - 区域道路
A
B
D
F
G
Layer 2 - 高速公路
长距离边
长距离边
A
D
G
搜索过程
#mermaid-svg-XXHakTGLn9krfFy1{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-XXHakTGLn9krfFy1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XXHakTGLn9krfFy1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XXHakTGLn9krfFy1 .error-icon{fill:#552222;}#mermaid-svg-XXHakTGLn9krfFy1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XXHakTGLn9krfFy1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XXHakTGLn9krfFy1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XXHakTGLn9krfFy1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XXHakTGLn9krfFy1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XXHakTGLn9krfFy1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XXHakTGLn9krfFy1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XXHakTGLn9krfFy1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XXHakTGLn9krfFy1 .marker.cross{stroke:#333333;}#mermaid-svg-XXHakTGLn9krfFy1 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XXHakTGLn9krfFy1 p{margin:0;}#mermaid-svg-XXHakTGLn9krfFy1 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-XXHakTGLn9krfFy1 .cluster-label text{fill:#333;}#mermaid-svg-XXHakTGLn9krfFy1 .cluster-label span{color:#333;}#mermaid-svg-XXHakTGLn9krfFy1 .cluster-label span p{background-color:transparent;}#mermaid-svg-XXHakTGLn9krfFy1 .label text,#mermaid-svg-XXHakTGLn9krfFy1 span{fill:#333;color:#333;}#mermaid-svg-XXHakTGLn9krfFy1 .node rect,#mermaid-svg-XXHakTGLn9krfFy1 .node circle,#mermaid-svg-XXHakTGLn9krfFy1 .node ellipse,#mermaid-svg-XXHakTGLn9krfFy1 .node polygon,#mermaid-svg-XXHakTGLn9krfFy1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XXHakTGLn9krfFy1 .rough-node .label text,#mermaid-svg-XXHakTGLn9krfFy1 .node .label text,#mermaid-svg-XXHakTGLn9krfFy1 .image-shape .label,#mermaid-svg-XXHakTGLn9krfFy1 .icon-shape .label{text-anchor:middle;}#mermaid-svg-XXHakTGLn9krfFy1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-XXHakTGLn9krfFy1 .rough-node .label,#mermaid-svg-XXHakTGLn9krfFy1 .node .label,#mermaid-svg-XXHakTGLn9krfFy1 .image-shape .label,#mermaid-svg-XXHakTGLn9krfFy1 .icon-shape .label{text-align:center;}#mermaid-svg-XXHakTGLn9krfFy1 .node.clickable{cursor:pointer;}#mermaid-svg-XXHakTGLn9krfFy1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-XXHakTGLn9krfFy1 .arrowheadPath{fill:#333333;}#mermaid-svg-XXHakTGLn9krfFy1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-XXHakTGLn9krfFy1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-XXHakTGLn9krfFy1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XXHakTGLn9krfFy1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-XXHakTGLn9krfFy1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XXHakTGLn9krfFy1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-XXHakTGLn9krfFy1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-XXHakTGLn9krfFy1 .cluster text{fill:#333;}#mermaid-svg-XXHakTGLn9krfFy1 .cluster span{color:#333;}#mermaid-svg-XXHakTGLn9krfFy1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-XXHakTGLn9krfFy1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-XXHakTGLn9krfFy1 rect.text{fill:none;stroke-width:0;}#mermaid-svg-XXHakTGLn9krfFy1 .icon-shape,#mermaid-svg-XXHakTGLn9krfFy1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XXHakTGLn9krfFy1 .icon-shape p,#mermaid-svg-XXHakTGLn9krfFy1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-XXHakTGLn9krfFy1 .icon-shape .label rect,#mermaid-svg-XXHakTGLn9krfFy1 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XXHakTGLn9krfFy1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-XXHakTGLn9krfFy1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-XXHakTGLn9krfFy1 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 查询向量
从最高层入口开始
贪心导航到最近节点
下降到下一层
继续贪心导航
下降到 Layer 0
在底层精细搜索
维护 ef 大小的候选集
从候选集取 TopK
参数详解
| 参数 | 阶段 | 作用 | 增大的影响 |
|---|---|---|---|
M |
构建 | 每个节点的最大邻居数 | 图更密,召回更好,内存更高 |
efConstruction |
构建 | 构建时的候选集大小 | 图质量更好,构建更慢 |
ef |
搜索 | 搜索时的候选集大小 | 召回更高,延迟更大 |
参数经验值:
| 场景 | M | efConstruction | ef |
|---|---|---|---|
| 低延迟优先 | 8-12 | 100 | 32-64 |
| 平衡 | 16 | 200 | 64-128 |
| 高召回优先 | 32-48 | 400 | 128-256 |
约束 :ef >= limit(TopK),否则候选集不够大。
内存估算
HNSW 内存 ≈ 原始向量 + 图结构
= N × dim × 4B + N × M × 2 × 8B
示例(100 万条,768 维,M=16):
= 1M × 768 × 4 + 1M × 16 × 2 × 8
= 2.87 GB + 0.24 GB
≈ 3.1 GB
在 Milvus 中使用
python
index_params.add_index(
field_name="embedding",
index_type="HNSW",
metric_type="COSINE",
params={"M": 16, "efConstruction": 200},
)
# 搜索时
search_params = {
"metric_type": "COSINE",
"params": {"ef": 128},
}
PQ(乘积量化)
PQ 的核心思想:把高维向量切成多段,每段用码本近似表示,大幅压缩存储。
编码过程
#mermaid-svg-T8pPf2gxrxjXQ9WK{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-T8pPf2gxrxjXQ9WK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-T8pPf2gxrxjXQ9WK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-T8pPf2gxrxjXQ9WK .error-icon{fill:#552222;}#mermaid-svg-T8pPf2gxrxjXQ9WK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-T8pPf2gxrxjXQ9WK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-T8pPf2gxrxjXQ9WK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-T8pPf2gxrxjXQ9WK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-T8pPf2gxrxjXQ9WK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-T8pPf2gxrxjXQ9WK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-T8pPf2gxrxjXQ9WK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-T8pPf2gxrxjXQ9WK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-T8pPf2gxrxjXQ9WK .marker.cross{stroke:#333333;}#mermaid-svg-T8pPf2gxrxjXQ9WK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-T8pPf2gxrxjXQ9WK p{margin:0;}#mermaid-svg-T8pPf2gxrxjXQ9WK .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-T8pPf2gxrxjXQ9WK .cluster-label text{fill:#333;}#mermaid-svg-T8pPf2gxrxjXQ9WK .cluster-label span{color:#333;}#mermaid-svg-T8pPf2gxrxjXQ9WK .cluster-label span p{background-color:transparent;}#mermaid-svg-T8pPf2gxrxjXQ9WK .label text,#mermaid-svg-T8pPf2gxrxjXQ9WK span{fill:#333;color:#333;}#mermaid-svg-T8pPf2gxrxjXQ9WK .node rect,#mermaid-svg-T8pPf2gxrxjXQ9WK .node circle,#mermaid-svg-T8pPf2gxrxjXQ9WK .node ellipse,#mermaid-svg-T8pPf2gxrxjXQ9WK .node polygon,#mermaid-svg-T8pPf2gxrxjXQ9WK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-T8pPf2gxrxjXQ9WK .rough-node .label text,#mermaid-svg-T8pPf2gxrxjXQ9WK .node .label text,#mermaid-svg-T8pPf2gxrxjXQ9WK .image-shape .label,#mermaid-svg-T8pPf2gxrxjXQ9WK .icon-shape .label{text-anchor:middle;}#mermaid-svg-T8pPf2gxrxjXQ9WK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-T8pPf2gxrxjXQ9WK .rough-node .label,#mermaid-svg-T8pPf2gxrxjXQ9WK .node .label,#mermaid-svg-T8pPf2gxrxjXQ9WK .image-shape .label,#mermaid-svg-T8pPf2gxrxjXQ9WK .icon-shape .label{text-align:center;}#mermaid-svg-T8pPf2gxrxjXQ9WK .node.clickable{cursor:pointer;}#mermaid-svg-T8pPf2gxrxjXQ9WK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-T8pPf2gxrxjXQ9WK .arrowheadPath{fill:#333333;}#mermaid-svg-T8pPf2gxrxjXQ9WK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-T8pPf2gxrxjXQ9WK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-T8pPf2gxrxjXQ9WK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-T8pPf2gxrxjXQ9WK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-T8pPf2gxrxjXQ9WK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-T8pPf2gxrxjXQ9WK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-T8pPf2gxrxjXQ9WK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-T8pPf2gxrxjXQ9WK .cluster text{fill:#333;}#mermaid-svg-T8pPf2gxrxjXQ9WK .cluster span{color:#333;}#mermaid-svg-T8pPf2gxrxjXQ9WK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-T8pPf2gxrxjXQ9WK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-T8pPf2gxrxjXQ9WK rect.text{fill:none;stroke-width:0;}#mermaid-svg-T8pPf2gxrxjXQ9WK .icon-shape,#mermaid-svg-T8pPf2gxrxjXQ9WK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-T8pPf2gxrxjXQ9WK .icon-shape p,#mermaid-svg-T8pPf2gxrxjXQ9WK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-T8pPf2gxrxjXQ9WK .icon-shape .label rect,#mermaid-svg-T8pPf2gxrxjXQ9WK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-T8pPf2gxrxjXQ9WK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-T8pPf2gxrxjXQ9WK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-T8pPf2gxrxjXQ9WK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 768 维向量
切成 m=96 段
每段 8 维
每段查码本
256 个聚类中心
每段用 1 字节编码
768 维 → 96 字节
压缩比 32:1
搜索过程
PQ 搜索不需要解码原始向量,而是用预计算的距离表快速估算近似距离:
#mermaid-svg-3ZXYYh0GTVhNzfZ7{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .error-icon{fill:#552222;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .marker.cross{stroke:#333333;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 p{margin:0;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .cluster-label text{fill:#333;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .cluster-label span{color:#333;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .cluster-label span p{background-color:transparent;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .label text,#mermaid-svg-3ZXYYh0GTVhNzfZ7 span{fill:#333;color:#333;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .node rect,#mermaid-svg-3ZXYYh0GTVhNzfZ7 .node circle,#mermaid-svg-3ZXYYh0GTVhNzfZ7 .node ellipse,#mermaid-svg-3ZXYYh0GTVhNzfZ7 .node polygon,#mermaid-svg-3ZXYYh0GTVhNzfZ7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .rough-node .label text,#mermaid-svg-3ZXYYh0GTVhNzfZ7 .node .label text,#mermaid-svg-3ZXYYh0GTVhNzfZ7 .image-shape .label,#mermaid-svg-3ZXYYh0GTVhNzfZ7 .icon-shape .label{text-anchor:middle;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .rough-node .label,#mermaid-svg-3ZXYYh0GTVhNzfZ7 .node .label,#mermaid-svg-3ZXYYh0GTVhNzfZ7 .image-shape .label,#mermaid-svg-3ZXYYh0GTVhNzfZ7 .icon-shape .label{text-align:center;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .node.clickable{cursor:pointer;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .arrowheadPath{fill:#333333;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .cluster text{fill:#333;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .cluster span{color:#333;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 rect.text{fill:none;stroke-width:0;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .icon-shape,#mermaid-svg-3ZXYYh0GTVhNzfZ7 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .icon-shape p,#mermaid-svg-3ZXYYh0GTVhNzfZ7 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .icon-shape .label rect,#mermaid-svg-3ZXYYh0GTVhNzfZ7 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3ZXYYh0GTVhNzfZ7 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 查询向量
切成 m 段
预计算:每段与 256 个中心的距离
对每条数据:查表累加 m 段距离
近似 TopK
参数详解
| 参数 | 作用 | 典型值 |
|---|---|---|
m |
子空间数量 | dim 的因子,如 768 维用 96 或 192 |
nbits |
每段编码位数 | 通常 8(256 个中心) |
压缩效果
| 原始大小 | PQ 编码 (m=96, nbits=8) | 压缩比 |
|---|---|---|
| 768 × 4B = 3072B | 96 × 1B = 96B | 32× |
| 1536 × 4B = 6144B | 192 × 1B = 192B | 32× |
代价
- 召回率下降(量化误差)
- 不适合小数据量(码本训练需要足够数据)
- 通常与 IVF 组合使用(IVF_PQ)
在 Milvus 中使用
python
index_params.add_index(
field_name="embedding",
index_type="IVF_PQ",
metric_type="L2",
params={"nlist": 1024, "m": 96, "nbits": 8},
)
DISKANN
DISKANN 把图索引存在磁盘上,只在内存中保留压缩的导航结构。适合超大规模、内存受限的场景。
#mermaid-svg-ou17n5j4wKFcXV5D{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ou17n5j4wKFcXV5D .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ou17n5j4wKFcXV5D .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ou17n5j4wKFcXV5D .error-icon{fill:#552222;}#mermaid-svg-ou17n5j4wKFcXV5D .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ou17n5j4wKFcXV5D .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ou17n5j4wKFcXV5D .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ou17n5j4wKFcXV5D .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ou17n5j4wKFcXV5D .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ou17n5j4wKFcXV5D .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ou17n5j4wKFcXV5D .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ou17n5j4wKFcXV5D .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ou17n5j4wKFcXV5D .marker.cross{stroke:#333333;}#mermaid-svg-ou17n5j4wKFcXV5D svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ou17n5j4wKFcXV5D p{margin:0;}#mermaid-svg-ou17n5j4wKFcXV5D .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ou17n5j4wKFcXV5D .cluster-label text{fill:#333;}#mermaid-svg-ou17n5j4wKFcXV5D .cluster-label span{color:#333;}#mermaid-svg-ou17n5j4wKFcXV5D .cluster-label span p{background-color:transparent;}#mermaid-svg-ou17n5j4wKFcXV5D .label text,#mermaid-svg-ou17n5j4wKFcXV5D span{fill:#333;color:#333;}#mermaid-svg-ou17n5j4wKFcXV5D .node rect,#mermaid-svg-ou17n5j4wKFcXV5D .node circle,#mermaid-svg-ou17n5j4wKFcXV5D .node ellipse,#mermaid-svg-ou17n5j4wKFcXV5D .node polygon,#mermaid-svg-ou17n5j4wKFcXV5D .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ou17n5j4wKFcXV5D .rough-node .label text,#mermaid-svg-ou17n5j4wKFcXV5D .node .label text,#mermaid-svg-ou17n5j4wKFcXV5D .image-shape .label,#mermaid-svg-ou17n5j4wKFcXV5D .icon-shape .label{text-anchor:middle;}#mermaid-svg-ou17n5j4wKFcXV5D .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ou17n5j4wKFcXV5D .rough-node .label,#mermaid-svg-ou17n5j4wKFcXV5D .node .label,#mermaid-svg-ou17n5j4wKFcXV5D .image-shape .label,#mermaid-svg-ou17n5j4wKFcXV5D .icon-shape .label{text-align:center;}#mermaid-svg-ou17n5j4wKFcXV5D .node.clickable{cursor:pointer;}#mermaid-svg-ou17n5j4wKFcXV5D .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ou17n5j4wKFcXV5D .arrowheadPath{fill:#333333;}#mermaid-svg-ou17n5j4wKFcXV5D .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ou17n5j4wKFcXV5D .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ou17n5j4wKFcXV5D .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ou17n5j4wKFcXV5D .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ou17n5j4wKFcXV5D .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ou17n5j4wKFcXV5D .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ou17n5j4wKFcXV5D .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ou17n5j4wKFcXV5D .cluster text{fill:#333;}#mermaid-svg-ou17n5j4wKFcXV5D .cluster span{color:#333;}#mermaid-svg-ou17n5j4wKFcXV5D div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ou17n5j4wKFcXV5D .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ou17n5j4wKFcXV5D rect.text{fill:none;stroke-width:0;}#mermaid-svg-ou17n5j4wKFcXV5D .icon-shape,#mermaid-svg-ou17n5j4wKFcXV5D .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ou17n5j4wKFcXV5D .icon-shape p,#mermaid-svg-ou17n5j4wKFcXV5D .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ou17n5j4wKFcXV5D .icon-shape .label rect,#mermaid-svg-ou17n5j4wKFcXV5D .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ou17n5j4wKFcXV5D .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ou17n5j4wKFcXV5D .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ou17n5j4wKFcXV5D :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 磁盘
内存
PQ 压缩向量
用于粗排
导航图入口
完整向量 + 图结构
查询
粗排候选集
精排 TopK
特性
| 维度 | 表现 |
|---|---|
| 内存 | 极低(仅 PQ 编码 + 导航结构) |
| 延迟 | 中等(涉及磁盘 IO) |
| 召回率 | 中高(PQ 粗排 + 原始向量精排) |
| 适用规模 | > 1 亿条 |
在 Milvus 中使用
python
index_params.add_index(
field_name="embedding",
index_type="DISKANN",
metric_type="COSINE",
)
# 搜索时
search_params = {
"metric_type": "COSINE",
"params": {"search_list": 100}, # 候选集大小
}
索引选择决策
#mermaid-svg-hrA3A42zPObEnhAQ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-hrA3A42zPObEnhAQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-hrA3A42zPObEnhAQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-hrA3A42zPObEnhAQ .error-icon{fill:#552222;}#mermaid-svg-hrA3A42zPObEnhAQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-hrA3A42zPObEnhAQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-hrA3A42zPObEnhAQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-hrA3A42zPObEnhAQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-hrA3A42zPObEnhAQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-hrA3A42zPObEnhAQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-hrA3A42zPObEnhAQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-hrA3A42zPObEnhAQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-hrA3A42zPObEnhAQ .marker.cross{stroke:#333333;}#mermaid-svg-hrA3A42zPObEnhAQ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-hrA3A42zPObEnhAQ p{margin:0;}#mermaid-svg-hrA3A42zPObEnhAQ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-hrA3A42zPObEnhAQ .cluster-label text{fill:#333;}#mermaid-svg-hrA3A42zPObEnhAQ .cluster-label span{color:#333;}#mermaid-svg-hrA3A42zPObEnhAQ .cluster-label span p{background-color:transparent;}#mermaid-svg-hrA3A42zPObEnhAQ .label text,#mermaid-svg-hrA3A42zPObEnhAQ span{fill:#333;color:#333;}#mermaid-svg-hrA3A42zPObEnhAQ .node rect,#mermaid-svg-hrA3A42zPObEnhAQ .node circle,#mermaid-svg-hrA3A42zPObEnhAQ .node ellipse,#mermaid-svg-hrA3A42zPObEnhAQ .node polygon,#mermaid-svg-hrA3A42zPObEnhAQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-hrA3A42zPObEnhAQ .rough-node .label text,#mermaid-svg-hrA3A42zPObEnhAQ .node .label text,#mermaid-svg-hrA3A42zPObEnhAQ .image-shape .label,#mermaid-svg-hrA3A42zPObEnhAQ .icon-shape .label{text-anchor:middle;}#mermaid-svg-hrA3A42zPObEnhAQ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-hrA3A42zPObEnhAQ .rough-node .label,#mermaid-svg-hrA3A42zPObEnhAQ .node .label,#mermaid-svg-hrA3A42zPObEnhAQ .image-shape .label,#mermaid-svg-hrA3A42zPObEnhAQ .icon-shape .label{text-align:center;}#mermaid-svg-hrA3A42zPObEnhAQ .node.clickable{cursor:pointer;}#mermaid-svg-hrA3A42zPObEnhAQ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-hrA3A42zPObEnhAQ .arrowheadPath{fill:#333333;}#mermaid-svg-hrA3A42zPObEnhAQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-hrA3A42zPObEnhAQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-hrA3A42zPObEnhAQ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hrA3A42zPObEnhAQ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-hrA3A42zPObEnhAQ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hrA3A42zPObEnhAQ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-hrA3A42zPObEnhAQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-hrA3A42zPObEnhAQ .cluster text{fill:#333;}#mermaid-svg-hrA3A42zPObEnhAQ .cluster span{color:#333;}#mermaid-svg-hrA3A42zPObEnhAQ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-hrA3A42zPObEnhAQ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-hrA3A42zPObEnhAQ rect.text{fill:none;stroke-width:0;}#mermaid-svg-hrA3A42zPObEnhAQ .icon-shape,#mermaid-svg-hrA3A42zPObEnhAQ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hrA3A42zPObEnhAQ .icon-shape p,#mermaid-svg-hrA3A42zPObEnhAQ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-hrA3A42zPObEnhAQ .icon-shape .label rect,#mermaid-svg-hrA3A42zPObEnhAQ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hrA3A42zPObEnhAQ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-hrA3A42zPObEnhAQ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-hrA3A42zPObEnhAQ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} < 10 万
10 万 - 500 万
500 万 - 5000 万
> 5000 万
是
否
是
否
选择索引
数据规模
FLAT
内存充足?
内存充足?
DISKANN 或 IVF_PQ
HNSW
IVF_FLAT 或 IVF_SQ8
HNSW
IVF_PQ
综合对比
| 索引 | 内存 | 构建速度 | 搜索速度 | 召回率 | 适用规模 |
|---|---|---|---|---|---|
| FLAT | 低 | 无需构建 | 慢 | 100% | < 10 万 |
| IVF_FLAT | 中 | 快 | 快 | 高 | 10 万 - 1000 万 |
| IVF_SQ8 | 较低 | 快 | 快 | 较高 | 10 万 - 1000 万 |
| HNSW | 高 | 中 | 很快 | 很高 | 10 万 - 5000 万 |
| IVF_PQ | 低 | 中 | 快 | 中 | > 1000 万 |
| DISKANN | 极低 | 慢 | 中 | 中高 | > 5000 万 |
选择原则
- 默认选 HNSW:大多数场景(< 2000 万条)HNSW 是最佳平衡
- 内存不够选 IVF_SQ8 或 IVF_PQ:用量化换内存
- 超大规模选 DISKANN:亿级数据的唯一选择
- 需要精确结果选 FLAT:评测基准或小数据量
索引构建与切换
查看当前索引
python
info = client.describe_collection("articles")
for index in info.get("indexes", []):
print(f"字段: {index['field_name']}, 类型: {index['index_type']}")
重建索引
Milvus 不支持原地修改索引参数。需要先删除再重建:
python
# 释放 Collection
client.release_collection("articles")
# 删除旧索引
client.drop_index(collection_name="articles", index_name="embedding_idx")
# 创建新索引
index_params = MilvusClient.prepare_index_params()
index_params.add_index(
field_name="embedding",
index_name="embedding_idx",
index_type="IVF_FLAT",
metric_type="COSINE",
params={"nlist": 2048},
)
client.create_index(collection_name="articles", index_params=index_params)
# 重新加载
client.load_collection("articles")
常见错误
| 现象 | 原因 | 修复 |
|---|---|---|
| HNSW 内存爆掉 | 数据量大 + M 值高 | 降低 M,或换 IVF/DISKANN |
| IVF 召回率低 | nprobe 太小 | 增大 nprobe,或增大 nlist |
| PQ 搜索结果差 | 数据量太少,码本训练不充分 | 数据 > 10 万条再用 PQ |
| 索引构建超时 | 数据量大 + efConstruction 高 | 降低 efConstruction,或增加 IndexNode 资源 |
| 搜索延迟不稳定 | ef/nprobe 设置过高 | 找到召回和延迟的平衡点 |
| DISKANN 延迟高 | 磁盘 IO 慢 | 使用 SSD,增大内存缓存 |
面试题
-
HNSW 为什么比 IVF 搜索更快但内存更高?
HNSW 用图结构导航,搜索路径短(O(log N)),但每个节点需要存储 M 个邻居指针。IVF 只存聚类中心,但搜索时需要扫描整个倒排列表。
-
IVF 的 nlist 设太大或太小分别有什么问题?
太大:每个列表太短,边界效应严重(相近向量被分到不同簇),需要更大 nprobe 补偿。太小:每个列表太长,搜索退化为暴力扫描。
-
PQ 为什么能压缩 32 倍但召回率只降几个点?
PQ 利用了向量各维度之间的统计独立性假设。子空间内用 256 个中心近似,误差在多个子空间累加后仍然可控。但对于维度间强相关的数据,PQ 效果会变差。
-
为什么 ef 必须 >= limit(TopK)?
ef 是搜索时维护的候选集大小。如果 ef < limit,候选集装不下 TopK 个结果,返回数量会不足。
-
什么场景下 FLAT 反而是最优选择?
数据量 < 10 万、需要 100% 召回率、或作为评测基准。此时 FLAT 的延迟可接受(< 50ms),且无需调参、无构建开销。
练习题
-
索引对比实验:准备 10 万条 768 维随机向量,分别用 FLAT、IVF_FLAT(nlist=256)、HNSW(M=16) 建索引。对比构建时间、内存占用和搜索延迟(固定 TopK=10)。
-
nprobe 调优:用 IVF_FLAT(nlist=1024) 索引 50 万条向量。nprobe 从 8、16、32、64、128、256 逐步增大,记录每个值的搜索延迟和召回率(以 FLAT 结果为基准)。画出 nprobe-recall 和 nprobe-latency 曲线。
-
HNSW 参数实验:固定 50 万条数据,分别用 M=8/16/32、efConstruction=100/200/400 的组合建索引。记录构建时间和内存。搜索时用 ef=64/128/256,记录延迟和召回率。
-
PQ 压缩效果:对比 IVF_FLAT 和 IVF_PQ 在 100 万条数据上的内存占用和召回率差异。
小结
向量索引是"用可控精度损失换取搜索加速"的工程工具。HNSW 是大多数场景的默认选择(高召回、低延迟、高内存),IVF 系列适合内存受限场景,PQ 和 DISKANN 面向超大规模。选择索引后,通过参数调优(ef、nprobe、M)在召回率和延迟之间找到业务可接受的平衡点。