Milvus 索引与运维
Written: 2026.06 上一讲:LangChain 生态系统下一讲:意图分类
1. 本讲目标
- 理解向量索引的本质:用空间和构建时间换查询速度
- 掌握四种主流索引类型(FLAT / IVF_FLAT / IVF_PQ/SQ8 / HNSW)的工作原理和适用场景
- 能用 PyMilvus 完成 Collection 创建、索引构建、数据插入、搜索的完整流程
- 理解 PyMilvus 原生混合检索和 langchain-milvus 混合检索的差异
- 知道本项目中 PyMilvus 和 langchain-milvus 分别负责哪些代码
- 能根据召回率、延迟和内存压力解释索引选型取舍
本讲定位:本讲用 PyMilvus 演示 Milvus 的创建、插入、索引、搜索和混合检索,再对照本项目的 langchain-milvus 封装。这里先讲离线构建的前置概览,完整的知识库构建链路(rebuild_kb_version.py、FAQ/文档入库、质量门禁、版本激活)放在第 16 讲系统展开。
2. 向量索引的本质
2.1 为什么需要索引
索引的本质:用额外的存储空间和构建时间,换取查询时的大幅加速。类比:- 无索引 = 在未排序的书架上逐本翻找
- 有索引 = 先查图书馆目录卡片,按索书号直接走到对应书架
2.2 索引在什么时候构建
关键点:- 创建 Collection 时不会自动建索引——必须显式调用
create_index() - 索引构建是异步的——调用
create_index()后立即返回,Milvus 在后台构建 - 必须先
load_collection()将索引加载到内存,才能使用索引加速搜索
3. 主流索引类型图解
数字口径说明:本节会出现两类数字。nlist、nprobe、M、efConstruction、ef这类参数范围来自 Milvus 官方文档;规模线、耗时和硬件容量只能作为教学示例或项目经验估算,不能当成官方标准。真实项目必须结合向量维度、过滤条件、QPS、硬件、collection/segment 状态和压测结果重新校准。
3.1 FLAT — 暴力搜索
3.2 IVF_FLAT — 倒排索引 + 暴力搜索
核心参数:3.3 IVF_SQ8 / IVF_PQ — 倒排索引 + 量化压缩
先记住一句话:IVF 负责“少查一些桶”,SQ8/PQ 负责“每个向量少占一点内存”。IVF_FLAT 只做聚类分桶,桶里的向量仍然以原始 float32 保存。IVF_SQ8 和 IVF_PQ 在 IVF 的基础上继续压缩向量,所以它们解决的核心问题不是“怎么更聪明地找桶”,而是“桶里的向量太多、太占内存怎么办”。 IVF_SQ8:Scalar Quantization,逐维压缩 SQ8 可以理解成“把每一维的小数压缩成 8 bit 编码”。原始向量每一维通常是 float32,占 4 字节;SQ8 会把每一维映射到 0-255 的整数区间,占 1 字节。这样向量主体存储会明显变小,但距离计算不再完全基于原始浮点数,因此召回率需要用评测集验证。
m 表示 PQ 分段数量,并要求向量维度能被 m 整除;nbits 表示每个低维子向量编码使用的 bit 数,默认常见为 8。
IVF_SQ8 和 IVF_PQ 的取舍
不要把 SQ8/PQ 理解成“更高级所以更好”。它们的本质是压缩。压缩带来内存收益,也会带来距离近似误差。是否值得用,要看业务对召回率、延迟、内存成本的取舍。
3.4 HNSW — 分层可导航小世界图
📖 深入学习:HNSW 图索引的理论原理详见 附录C:HNSW 索引参数调优。HNSW 和 IVF 的思路完全不同:
- IVF 是“先聚类分桶,再只查少数桶”。
- HNSW 是“把向量组织成图,搜索时沿着越来越近的节点走”。
3.5 HNSW 是怎么建出来的
构建 HNSW 时,每个向量会变成图里的一个节点。新节点插入时,会在图中寻找离它比较近的已有节点,并建立连接。不是每个节点都出现在所有层:- 最底层包含全部向量。
- 越往上,节点越少。
- 上层负责快速跳转,下层负责精细搜索。
3.6 HNSW 是怎么查的
查询时,HNSW 不会从底层全量扫描开始,而是从上层入口点开始:
这些参数的默认值由当前 Milvus / langchain-milvus 版本和创建方式决定,不应在讲义里当成固定标准。需要确认时,看 collection 的 index 描述。
HNSW 参数调小/调大的直觉
3.7 索引选型决策树
分支一:小规模 / 强过滤后候选少 → FLAT FLAT 不做任何近似——逐条计算与全部向量的距离后排序返回。优势是 100% 精确,适合原型验证、离线评测基准、强过滤后候选集很小的场景。本项目的单个场景 FAQ Collection 通常只有几十到几百条,在这个数量级上 FLAT 和 HNSW 的延迟差异通常不明显,但这仍然要以本机压测为准。 分支二:中等规模 + 内存充足 → HNSW(本项目选择) HNSW 是常用的高召回低延迟 ANN 索引。它预先构建多层”高速公路图”——上层节点少跳得远,下层节点密查得准。Milvus 官方口径是:HNSW 查询延迟低、搜索准确性好,但需要更高内存来维护图结构。本项目选择 HNSW,是因为当前知识库规模不大,且更看重召回稳定性;具体内存和延迟不能照抄固定数字,应以容量估算和压测为准。 分支三:内存更敏感 → IVF_FLAT 用 K-means 聚类分桶,检索时只搜最近 N 个桶。内存比 HNSW 小(不需要存储图结构),但精度略低——查询向量落在桶边界附近时可能漏掉相邻桶中的近邻。 分支四:大规模 / 内存压力明显 → IVF_PQ 或 DiskANN 当向量规模继续扩大、内存成本成为主要瓶颈时,才考虑 IVF_PQ、DiskANN 等方案。IVF_PQ 通过量化压缩减少内存,DiskANN 将部分索引压力转移到 SSD。它们不是“规模一大就必选”,而是需要结合召回率目标、SSD 性能、过滤条件和压测结果来定。4. PyMilvus 基本操作与原生混合检索
以下代码展示不依赖 LangChain、直接用 PyMilvus 操作 Milvus 的完整流程。理解这些后,再看第 8 讲中 langchain-milvus 的封装,就能知道底层发生了什么。4.1 连接 Milvus
4.2 创建 Collection 和 Schema
4.3 创建索引
4.4 插入数据
4.5 加载到内存并搜索
4.6 删除数据
4.7 完整流程串联
4.8 用 PyMilvus 直接实现混合检索
前面的collection.search() 只查一个向量字段。真实 RAG 项目常常需要同时查两路:
dense:语义向量,适合相似表达和改写。sparse:BM25 稀疏向量,适合关键词、编号、术语、制度名称。
pk、text、dense 和业务元数据。sparse 是 BM25 Function 的输出字段,由 Milvus 根据 text 自动生成,不需要手动传入。
dense_request负责语义召回。sparse_request负责关键词召回。WeightedRanker(0.55, 0.45)负责把两路结果按权重融合。expr负责 source、版本、租户、可见性等标量过滤。
5. langchain-milvus 如何实现混合检索
上下文:第 3 讲 已经建立了 VectorStore 抽象;本讲先用 PyMilvus 展示 Milvus 的底层操作,再回到本项目的 langchain-milvus 封装。这样你能理解”为什么项目代码中没有显式的理解了上面的 PyMilvus 原生混合检索后,再看下面的create_collection()或create_index()调用”。
Milvus()、add_documents()、similarity_search_with_score(),就能知道 langchain-milvus 帮我们省掉了哪些重复代码。第 8 讲会在完整 Hybrid Search 场景中再次使用这些封装。
5.1 初始化时的隐藏操作
create_collection() 或 create_index() 调用:常规创建和插入流程由 langchain-milvus 封装完成;项目只在 schema 校验、database 管理、重建 collection 等地方直接使用 PyMilvus。
在本项目里,这段代码位于 qa_core/retrieval/store.py::MilvusHybridStore.store。它是 FAQ 集合和文档集合的统一检索入口。
5.2 add_documents() 的隐藏操作
scripts/rebuild_kb_version.py 和 scripts/rebuild_scenarios.py 会通过检索封装把 FAQ 和文档 chunk 写入 Milvus。第 16 讲会完整展开入库链路;本讲只需要先知道:项目最终不是手写 collection.insert(),而是通过 MilvusHybridStore.add_documents() 写入。
5.3 similarity_search_with_score() 的隐藏操作
qa_core/retrieval/store.py::MilvusHybridStore.search()。项目还会在 LangChain 返回结果之后继续做两件事:
- 转成项目内部的
RetrievalHit,避免上层业务直接依赖 langchain-milvus 的返回结构。 - 按需调用 CrossEncoder reranker,对初始候选做二阶段重排。
langchain-milvus==0.2.2 调用方式保持一致。后续如果升级到新的 reranker Function API,再把这里的 ranker_type/ranker_params 替换为新写法。
5.4 对比总结
PyMilvus 原生混合检索 vs langchain-milvus 混合检索:6. langchain-milvus 与 PyMilvus 的职责边界
6.1 本项目为什么两者都存在
本项目最终选择:继续使用 langchain-milvus 作为业务检索入口,保留 PyMilvus 作为底层连接、database 管理和 schema 检查工具,不迁移为纯 PyMilvus 实现。 原因是langchain-milvus 不是独立驱动——它是套在 PyMilvus 之上的 LangChain VectorStore。业务代码面向 LangChain VectorStore,但底层连接、database、collection schema 仍然由 PyMilvus 完成。
这里的“适配层”不是为了兼容旧版本而额外凑出来的代码,而是职责边界:
- 业务检索要接 LangChain 的
Document、embedding、reranker 和 QAService,所以入口放在 langchain-milvus。 - Milvus 连接、database、BM25 Function、schema 检查属于数据库驱动层,放在 PyMilvus 相关工具里更清楚。
- 当 collection 结构不符合当前 Dense + BM25 Sparse 设计时,必须靠底层 schema 检查及时报错,不能让业务层悄悄降级。
6.2 本项目当前的稳定做法
适配代码集中在qa_core/retrieval/milvus_compat.py:
MilvusHybridStore.store 在首次创建 wrapper 时做三件事:
如果已经用了 LangChain Milvus,为什么还要导入 PyMilvus?
答案:LangChain Milvus 是抽象层,不是底层驱动。抽象层让 RAG 好写,底层驱动负责连接、database 和 collection schema。项目用一个很薄的适配层把这些底层细节收口。
6.3 本项目代码职责地图
这样划分后,可以这样理解:
7. 调参与性能提醒
本讲不要求记固定耗时,也不把“多少条向量用什么索引”讲成死规则。索引效果要看自己的数据、向量维度、过滤条件、QPS、硬件和评测集。真正上线前,至少要同时观察四个指标:召回率、查询 P95、索引大小、构建时间。 本讲先理解取舍关系即可:8. 本讲实践闭环
通过标准:能解释 Collection、Field、Index、load、search 分别做什么。
8.1 本讲从 0 到 1 实现闭环
这一讲是底层实验,不直接交付最终项目业务代码。它的作用是解释第 8 讲MilvusHybridStore 背后到底封装了什么。
- 先用 PyMilvus 连接 Milvus。
- 再创建一个最小 collection,包含主键、文本、dense 向量字段。
- 然后创建向量索引。
- 插入几条样本向量后
load_collection()并执行 Top-K 搜索。 - 再理解 PyMilvus 原生混合检索如何把 dense 和 sparse 两路结果融合。
- 最后删除实验 collection,避免污染后续项目数据。
9. 重点掌握
10. 本讲小结
- 索引的本质:用空间和构建时间换查询速度。没索引走暴力搜索(FLAT),有索引走 ANN 近似搜索
- 四种主流索引:FLAT(100% 精度/最慢)、IVF_FLAT(聚类加速)、IVF_PQ(压缩+聚类)、HNSW(图搜索/本项目使用)
- 选型决策:小规模或强过滤后候选少→FLAT;重视低延迟和高召回且内存充足→HNSW;内存更敏感→IVF_FLAT;大规模且内存压力明显→IVF_PQ/DiskANN
- PyMilvus 基本操作:连接 → 创建 Collection → 创建索引 → 插入 → 加载 → 搜索
- PyMilvus 原生混合检索:显式构造 dense/sparse 两路请求,再用 ranker 融合
- langchain-milvus 自动帮我们做了:初始化时自动 Schema+Index+Load;add_documents 时自动 Embedding+BM25+Insert;search 时自动 Embedding+混合融合
- 本项目职责边界:
store.py负责业务检索封装,milvus_compat.py负责连接、database 和 BM25 Function,入库脚本负责重建和写入 - 调参要看评测:当前项目规模先用默认 HNSW;生产优化必须看压测和召回评测,不靠固定数字拍板

