db-frontier/08
讲清了 HNSW、DiskANN、IVF-PQ
的图/倒排假设与召回–QPS–内存三角。第 8 篇 讲清了
Knowhere 的 VecIndex 类层次与工厂。中间还缺一张
工程师调参时看见的接口表:集合上声明哪种索引、哪些参数属于构建期、哪些属于查询期、Flat
在引擎里扮演什么角色。
本文不重证小世界图性质,只建立 算法名 → 引擎契约 的翻译层。
本文是「向量检索引擎」系列第 2 篇(共 19 篇)。→ 系列目录
版本锚定:Milvus 2.6.x Knowhere / Architecture;算法细节外链 frontier/08。
一、最小故事:你改的到底是哪一类旋钮
假设 Collection 已建好 HNSW。业务说「召回不够」,同学 A
把 ef 从 64 调到 128;同学 B 把 M
从 16 调到 32。两者都可能改善召回,但
运维代价完全不同:
ef是 查询期 参数:改请求即可,不必重建索引。M/efConstruction是 构建期 参数:通常要等 Data Node 重建 Sealed 上的索引,再 handoff。
排障时若分不清这两类旋钮,就会把「改配置立刻生效」的期望套到必须重建的参数上——延迟尖刺与召回掉点会一起出现。本文先把契约钉住,再把类型表与 Segment / 过滤接点讲清。
二、引擎需要的不是论文,是四个动词
生产路径里,ANN 对引擎暴露的能力可以收成:
| 动词 | 含义 | 典型发生位置 |
|---|---|---|
| Train | 需要训练的结构(如 IVF 质心)在数据上拟合 | Data Node 建索 / Knowhere |
| Build / Add | 把段内向量写入索引结构 | 同上 |
| Serialize / Load | 索引落对象存储,Query Node 加载 | 第 6、7、10 篇 |
| Search / Query | 带可选 bitset 掩码的近似 Top-\(k\) | Segcore → Knowhere |
Knowhere 文档强调:与经典「训练集 / 测试集分离」不同,段内数据既用于 train 也用于 search。因此索引生命周期 贴着 Sealed segment,而不是贴着全局离线竞赛设定(第 3、10 篇)。
flowchart LR
sealed["Sealed segment"]
train["Train"]
build["Build / Add"]
ser["Serialize to object store"]
load["Load on Query Node"]
search["Search / Query + bitset"]
sealed --> train --> build --> ser --> load --> search
2.1 召回–QPS–内存三角(接口层示意)
frontier/08 已给出算法层三角。引擎侧只记住:任意索引类型都在三点之间取舍,调参是在三角形边上滑动,不是同时把三点推到最优。
flowchart TB
recall["Recall"]
qps["QPS / latency"]
mem["Memory / disk"]
recall --- qps
qps --- mem
mem --- recall
本图 不含数字:具体曲线依赖维度、数据集与硬件。线上调参应用自己的 golden set 测 Recall@\(k\) 与延迟中位数(第 17 篇),不要抄营销页上的「提升 30%」。
三、类型速查:算法假设 → 引擎选型直觉
下表只给 接口层直觉;复杂度与参数精调见 frontier/08 与 llm-infra/18。
| 引擎侧常见类型 | 算法家族 | 内存/磁盘假设 | 何时优先考虑 |
|---|---|---|---|
| FLAT / IDMAP | 暴力 | 全向量常驻计算 | 召回金标准、小段、调参对照 |
| IVF_FLAT / IVF_PQ / IVF_SQ* | 倒排 + 可选量化 | 列表扫描;PQ 换内存 | 可接受训练、偏吞吐/压缩 |
| HNSW | 分层小世界图 | 高内存图 | 低延迟内存检索主流默认 |
| DiskANN 等 | 磁盘友好图 | 索引主要在盘/对象侧 | 单机内存装不下全体图 |
| 二进制族 | Hamming 等 | 压缩二值向量 | 指纹/化学等二值特征 |
Architecture Overview 写明 Milvus 构建在
Faiss、HNSW、DiskANN、SCANN 等库之上;具体 release 启用哪些
index_type 字符串以当时 Index 文档为准。
3.1 构建期 vs 查询期参数
| 阶段 | 例子(概念名) | 工程含义 |
|---|---|---|
| 构建期 | HNSW 的 M、efConstruction;IVF
的 nlist |
改了通常要 重建索引 |
| 查询期 | HNSW 的 ef;IVF 的 nprobe |
可按请求调节延迟/召回,不必重建 |
把查询期参数当成「免费旋钮」、把构建期参数当成「变更窗口」——排障时先分清改的是哪一类。
3.2 Flat 的系统角色
Knowhere 把 IDMAP 也做成
VecIndex:无
train/build,查询直接打原始向量。系统用途:
- 离线算 Recall@\(k\) 的 ground truth;
- 小数据或索引未就绪时的正确性路径;
- 对比「加索引后掉了多少召回」的基线。
不要在十亿级集合上把 Flat 当默认在线索引。
3.3 教学示意:同一段上的两种搜索
设某 Sealed 段有 \(N=10^5\) 向量。Flat
对每次查询做约 \(N\)
次距离;HNSW 在合适 ef
下只访问图的一个子图。引擎不会自动告诉你「这段该用哪种」——选型是
schema / index 配置决策,代价在 Data Node 建索与 Query Node
内存上显现(第 8、10 篇)。
3.4 参数落点:SDK 字符串如何进 Knowhere
工程师在 SDK 里通常看到两层配置(名称以官方 Index 文档为准,随 release 微调):
| 配置面 | 典型字段 | 落到契约的哪一动词 |
|---|---|---|
index_type |
HNSW / IVF_FLAT /
DISKANN / … |
选择哪一个 VecIndex 实现 |
metric_type |
L2 / IP / COSINE
等 |
距离定义;与向量是否归一化强相关 |
构建 params |
M、efConstruction、nlist、… |
Train / Build |
查询 params |
ef、nprobe、… |
Search |
度量与归一化:IP /
COSINE
混用、或未归一化却声明余弦,会在「参数看起来合理、召回却怪」的案例里反复出现——第
17
篇排障清单会再钉一次。本篇只要求:度量是契约的一部分,不是事后装饰。
3.5 Growing 上还没有「最终索引」时搜什么
Data Processing 把实时可搜钉在 Growing 路径:WAL
提交后即可参与查询,但 最终 ANN
结构(例如完整 HNSW)往往在 Sealed 上由 Data Node
构建。引擎可能在 Growing
侧使用更轻的临时结构或暴力路径(具体以当时 release 的
interim index 配置为准,见
queryNode.segcore.*,第 7 篇)。
对学生的含义:「Collection 声明了 HNSW」≠「刚写入的那一行已经在一张建好的 HNSW 里」。声明描述的是 Sealed 目标形态;实时路径另有可搜保证。
四、与 Segment / 过滤的接口接点
- 每 Sealed 一段一索引(Data
Processing)→ 全局一次
search= 多索引候选归并(第 9 篇)。 - bitset 软删与属性过滤(Bitset /
Knowhere)→
Search必须接受「哪些行仍参与」的掩码(第 11、13 篇)。 - Growing 路径可能尚无最终 ANN 结构 → 实时可搜 ≠ 最终索引形态(第 5、10 篇)。
选择度很低的标量过滤会让「局部 Top-\(k\) 再合并」失效——这是算法三角之外的第四个变量,展开见第 11 篇与 db-frontier/09。
flowchart TB
expr["Scalar expression"]
bitset["Filter + delete bitset"]
kh["Knowhere Search"]
reduce["Segment / shard / Proxy reduce"]
expr --> bitset --> kh --> reduce
五、常见误解
- 「调大
ef/nprobe就等于换了更好的索引」。查询期参数只在 已有图/倒排结构 上多探一些邻居;结构本身由构建期参数决定。 - 「Collection 上声明一次 HNSW,全集群共享一张图」。实现是 每 Sealed segment 一份索引;Growing 路径另算。
- 「Flat 太慢所以线上永远不该出现」。评估召回时 Flat(或等价精确路径)仍是金标准;问题在于 把 Flat 当默认生产索引。
六、插件契约(回指第 8 篇)
向 Knowhere
增加索引类型的官方步骤:IndexEnum →
ConfAdapter 校验 → 实现 VecIndex →
VecIndexFactory → unittest。量化系参考
IVF_FLAT,图系参考 HNSW,树系参考
Annoy。
本篇对应用的含义:你在 SDK 里填的 index_type
/ params,最终要落到某一 VecIndex
实现的
Train/Query;没有实现的字符串不是「配置写错了就会自动降级」的承诺。
七、学术谱系、工程间隙与开放问题
| 层 | 代表 | 本文角色 |
|---|---|---|
| 算法 | Malkov HNSW;DiskANN;IVF-PQ | 外链 frontier/08 |
| 库 | Faiss / Hnswlib | Knowhere 之下 |
| 契约 | VecIndex 四动词 + 段生命周期 |
本篇 |
| 系统 | Milvus Workers | 第 7–10 篇 |
谱系分叉点:ANN 论文优化「给定静态 \(X\) 建一个索引再查」;向量 DBMS(SIGMOD 2021)要求动态更新。Knowhere 的段内 Train/Search 同数据,是后者在索引插件层的落点——不要用静态 benchmark 的最优参数当生产默认值而不做过滤与多段评测。
工程间隙:
- 论文 benchmark 常假设静态全集建一个索引;Milvus 上是动态分段 + 归并 + bitset。
- 把 ann-benchmarks 上的最优
ef直接抄到生产,可能忽略过滤与多段归并。 - GPU / 磁盘索引的 Load 语义与 CPU 内存图不同:同一套 SDK
index_type背后是不同资源模型(第 8、18 篇)。
开放问题:
- 多向量字段时,单段多索引的参数面如何避免组合爆炸?
- 磁盘索引与对象存储冷热分层如何共用同一套 Load 语义(第 6、18 篇)?
- 过滤选择度进入契约后,查询期参数是否应随谓词自适应(算法侧见 frontier/09;引擎侧见第 11 篇)?
八、小结
三句话小结
- ANN 进引擎后变成 Train / Build / Load / Search 契约,索引贴着 Sealed segment。
- 构建期与查询期参数运维代价不同;Flat 是金标准,不是默认线上索引。
- 过滤 bitset 与多段归并是三角之外的第四个变量——见第 11、9 篇。
下一篇进入数据模型:Segment 状态机;Knowhere 实现见 第 8 篇。
参考资料
- Milvus Documentation v2.6.x, Knowhere、Architecture Overview、Data Processing。
- db-frontier/08、db-frontier/09。
- 第 8 篇 Knowhere、系列 index。
返回 系列目录 | 上一篇:向量引擎全景 | 下一篇:Segment 状态机
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【向量检索引擎】Knowhere:向量索引执行引擎与插件契约
按官方 Knowhere 文档说明其在 Milvus 中的位置、相对 Faiss 的扩展(bitset、SIMD 选择、二进制度量)、VecIndex 类层次与 IDMAP/IVF/HNSW 等类型,用插件注册、CPU/GPU 分发与 bitset 进查询三张图钉住工程契约,并与 db-frontier/08 的算法细节分工。
【向量检索引擎】向量引擎全景:算法、RAG 与专用引擎之间的一层
定位专用向量检索引擎相对 ANN 算法、RAG 应用与湖仓格式的分工;以 Milvus 2.6.x 四层架构与 insert/search 最小故事建立坐标系,并交代从 SIGMOD 2021 到 Streaming 演进的谱系与常见误解。
【向量检索引擎】Milvus · Segcore · Knowhere · Qdrant · Lance · pgvector
补齐 ANN 算法与 RAG 应用之间的生产级向量引擎层:以 Milvus 2.6.x 为主线拆解 Segment、WAL、Segcore、Knowhere、混合过滤与一致性,并用 Qdrant、LanceDB、pgvector 对照选型。
【向量检索引擎】Qdrant 对照:单库路径、payload 过滤与分片副本
对照 Qdrant 与本系列 Milvus 主线:Segment 内的向量/payload/id mapper 三件套、WAL+序号版本化、后台 optimizer 四类任务,以及 Raft 只管拓扑不管点操作的分布式模型;说明何时单进程/小集群更合适。