【学习笔记】Milvus 是什么?为什么 RAG 系统经常使用它?-14

当企业知识库从几千个文档增长到数百万个文本片段,RAG 系统面临的问题就不再只是"能不能查到",而是:向量如何存储、索引如何构建、查询如何扩展,以及检索服务是否会拖慢业务数据库。

Milvus 就是为这类向量检索场景设计的开源向量数据库。它不负责生成答案,也不是 Embedding 模型,而是接收向量、建立索引,并根据查询向量找出最相似的数据。

一、Milvus 在 RAG 中负责哪一段?

一次典型的知识库问答可以拆成以下链路:

复制代码
用户问题
   ↓
Embedding 模型:问题 → 查询向量
   ↓
Milvus:向量相似度检索
   ↓
返回 chunk_id、文本标识和相似度
   ↓
PostgreSQL:查询权限、版本和业务元数据
   ↓
大模型:根据召回内容生成回答

Milvus 的核心职责是"高效找相似向量"。原始文件通常放在 MinIO、S3 或 OSS,用户、文档、权限和任务状态通常放在 PostgreSQL,Milvus 中保存向量以及用于检索关联的字段。

二、Milvus 为什么会带着 etcd、MinIO 和 Standalone?

第一次用 Docker Compose 启动 Milvus 时,常常会看到三个容器:milvus-etcd、milvus-minio 和 milvus-standalone。这不是把三个向量数据库启动起来,而是一个 Milvus 实例依赖的几个角色:

组件 主要职责 不负责什么
etcd 保存 Collection、Schema、Segment、索引等元数据,并参与服务注册和健康检查 不保存完整文本,也不执行向量相似度搜索
MinIO / S3 保存 Milvus 的持久化对象,例如数据文件、索引文件和部分日志/WAL相关对象 不负责租户权限,也不是 Milvus 的查询 API
Milvus Standalone 在单机进程中运行 Milvus 的主要服务,对外提供 19530 等访问入口 不是高可用集群,也不是业务应用层

可以用下面这张图理解它们的关系:

复制代码
应用 / PyMilvus
       │ 19530
       ▼
Milvus Standalone
   ├── 读取 etcd:知道有哪些集合、分区、索引和服务
   ├── 读取 MinIO:加载数据文件和索引文件
   └── 执行插入、加载、过滤和向量检索

PostgreSQL:业务元数据、权限、任务状态
应用自己的 MinIO:PDF、Word、图片等原始文件(可与 Milvus 存储分开)

这里有一个容易被忽略的边界:Milvus 使用的 MinIO 与 RAG 应用保存原始文件的 MinIO 可以是同一个 MinIO 服务,但最好使用不同的 Bucket 或前缀,并分别管理权限、生命周期和备份。不要因为"都叫 MinIO"就把两类数据混在一起。

Standalone 是什么部署模式?Standalone 表示单机部署形态,不是一个独立的功能模块。它把多个 Milvus 服务角色收拢到一个 Milvus 进程或容器中,适合本地开发、功能验证和中小规模内部服务。官方 Docker Compose 模板通常会同时启动 Milvus、etcd 和 MinIO;某些安装脚本还会把 etcd 嵌入 Milvus 容器中,具体组合会随 Milvus 版本变化。

因此,Standalone 可以理解为"方便启动的单机版 Milvus",而不是"生产集群的高可用替代品"。当查询、写入、索引构建和故障恢复需要独立扩展时,应评估 Distributed 或 Kubernetes 部署。

三、它和普通数据库有什么不同?

传统数据库擅长精确查询:

复制代码
SELECT * FROM documents WHERE document_id = 'doc-001';

向量数据库擅长近似查询:

复制代码
给定查询向量 q,找出距离 q 最近的 Top K 个向量。

二者的查询目标不同。用户问"报销差旅住宿标准是什么",系统通常不会拿这句话做完全相等匹配,而是将问题和文档片段都转换成向量,再根据语义距离寻找相关内容。

四、Milvus 的主要能力

4.1. 保存多种向量和标量字段

一个实体可以包含主键、文本片段、租户编号、文档编号和向量字段。向量可以是稠密向量,也可以根据版本和场景使用稀疏向量、二进制向量等类型。

4.2. 通过索引降低搜索成本

最直接的 FLAT 会逐个比较向量,结果精确但数据量大时成本高。IVF、HNSW 等索引通过减少候选范围,换取更低的查询延迟。

4.3. 支持向量检索与标量过滤

例如只检索某个租户、某个部门或已发布文档:

复制代码
results = client.search(
    collection_name="knowledge_chunks",
    data=[query_vector],
    limit=5,
    filter='tenant_id == "tenant-a" and status == "published"',
    output_fields=["document_id", "chunk_id", "text"],
)

过滤条件不是权限系统的替代品。生产环境仍应在应用层和业务数据库中完成权限判断,并将安全范围作为检索条件的一部分。

4.4. 支持独立扩展

Milvus 采用计算与存储分离的架构,查询节点、数据节点和索引节点可以根据负载进行扩展。这使它更适合向量检索已经成为核心负载的系统。

五、一个最小的 Milvus 检索示例

安装 Python SDK:

复制代码
pip install pymilvus

连接 Milvus Lite、Standalone 或远程服务时,连接地址不同,但客户端调用方式可以保持相近:

复制代码
from pymilvus import MilvusClient

client = MilvusClient(uri="http://localhost:19530")

query_vector = [0.01, 0.12, 0.34]  # 实际维度必须和集合定义一致

results = client.search(
    collection_name="knowledge_chunks",
    data=[query_vector],
    anns_field="embedding",
    limit=3,
    output_fields=["document_id", "chunk_id"],
)

for hits in results:
    for hit in hits:
        print(hit["id"], hit["distance"], hit["entity"])

注意:示例中的向量维度只是演示。真实项目必须由 Embedding 模型确定维度,并保证写入向量和查询向量使用同一模型、同一归一化规则和同一距离指标。

六、Milvus 不会自动解决哪些问题?

Milvus 不能替你完成以下工作:

  • PDF、Word、Excel 的解析和 OCR;

  • 文本切分、Embedding 生成和模型版本管理;

  • 文档权限、租户隔离和数据脱敏;

  • 召回结果是否真正回答了用户问题;

  • 业务数据库与向量数据的同步;

  • RAG 最终答案的评估和引用生成。

因此,Milvus 只是 RAG 检索链路中的一个基础设施组件。把它部署起来,不等于知识库就会自动变准确。

七、什么时候值得使用 Milvus?

优先考虑以下情况:

  1. 向量数量和查询并发持续增长;

  2. 向量检索已经明显影响 PostgreSQL 等业务数据库;

  3. 需要专门的索引、分片和查询节点扩展能力;

  4. 需要同时处理多种向量或混合检索场景;

  5. 团队能够承担额外的部署、监控、备份和升级成本。

如果项目仍处于原型阶段,且业务数据与向量数据关联紧密,PostgreSQL 加 pgvector 可能更简单。选型应以压测和运维能力为依据,而不是只看产品名气。

八、从本地启动到连接验证

如果只是验证 Milvus 的集合、插入和检索能力,优先使用官方提供的 Docker Compose 文件,不要手动拼接一套未经验证的 etcd、MinIO 和 Milvus 版本组合:

复制代码
# 以官方文档当前提供的版本文件为准
wget https://github.com/milvus-io/milvus/releases/download/v3.0.0/milvus-standalone-docker-compose.yml -O docker-compose.yml
docker compose up -d
docker compose ps

启动后,应用通常只连接 Milvus 的 19530 端口;etcd 和 MinIO 是 Milvus 的内部依赖,不应直接暴露给公网。检查时重点看三个服务是否健康、数据卷是否持久化,以及 Milvus 日志中是否出现连接 etcd 或对象存储失败。

复制代码
docker compose logs --tail 100 standalone
docker compose logs --tail 100 etcd
docker compose logs --tail 100 minio

如果使用的是嵌入式 Standalone 脚本,服务名和日志命令可能不同;文章中的命令用于说明排查思路,实际以下载到的 Compose 文件为准。停止容器和删除数据卷是两件事,测试环境也不要把 down 与删除 volumes 混为一谈。

九、上线前的基本检查

  • 明确向量维度和距离指标;

  • 为每条 Chunk 设计稳定的业务 ID;

  • 设计文档更新、删除和权限变更的同步机制;

  • 使用真实问题集测试 Recall@K 和 P95 延迟;

  • 区分 Milvus 检索结果与最终答案质量;

  • 准备备份、重建索引和失败重试方案。

刚接触 Milvus 时,最容易混淆的三个概念是 Collection、Partition 和向量字段。它们分别对应"数据集合""集合内部的组织方式"和"用于相似度计算的字段"。如果概念没有分清,后续的插入、加载、检索和扩容都会变得混乱。

在实际部署中,还要把"数据模型"和"运行依赖"分开理解:Collection、Partition、Field 是 Milvus 面向开发者的数据抽象;etcd、MinIO、Standalone 是 Milvus 的运行架构。

前者决定数据怎么组织,后者决定这些数据的元信息、文件和服务如何运行。

复制代码
数据模型:Collection → Partition → Entity → Field
运行依赖:Standalone / Milvus 服务
             ├── etcd:元数据、服务注册与健康检查
             └── MinIO / S3:数据文件、索引文件等对象

十、先用关系型数据库类比

可以先做一个不完全等价但很有帮助的类比:

复制代码
Milvus Collection ≈ 一张结构化数据表
Milvus Entity     ≈ 一行记录
Milvus Field      ≈ 一列
Milvus Partition  ≈ 表内部的一组逻辑分区

但 Collection 不是简单的 SQL 表。它的核心任务是组织向量实体,并为向量字段建立索引。

十一、Collection:一类数据的容器

在知识库中,可以把一个 Collection 设计成"知识片段集合":

复制代码
knowledge_chunks
├── chunk_id
├── document_id
├── tenant_id
├── text
├── status
└── embedding

Collection 创建时需要定义 Schema。Schema 至少要明确主键和向量字段的类型、维度;其他字段用于过滤、回填和关联业务数据。

Collection 的 Schema 和元数据由 Milvus 管理,并由其元数据存储维护;应用不应该直接操作 etcd 中的键值。etcd 是 Milvus 的内部依赖,不是给业务代码查询 Collection 的管理数据库。需要查看集合、索引和加载状态时,应使用 Milvus SDK、CLI 或管理 API。

一个简化示例:

复制代码
from pymilvus import DataType, MilvusClient

client = MilvusClient(uri="http://localhost:19530")
schema = client.create_schema(auto_id=False, enable_dynamic_field=False)
schema.add_field("chunk_id", DataType.VARCHAR, max_length=64, is_primary=True)
schema.add_field("document_id", DataType.VARCHAR, max_length=64)
schema.add_field("tenant_id", DataType.VARCHAR, max_length=64)
schema.add_field("text", DataType.VARCHAR, max_length=8192)
schema.add_field("embedding", DataType.FLOAT_VECTOR, dim=768)

index_params = client.prepare_index_params()
index_params.add_index(
    field_name="embedding",
    index_type="HNSW",
    metric_type="COSINE",
    params={"M": 16, "efConstruction": 200},
)

client.create_collection(
    collection_name="knowledge_chunks",
    schema=schema,
    index_params=index_params,
)

示例中的 dim=768 只是占位值,必须替换成实际 Embedding 模型的输出维度。

十二、Field:向量字段和标量字段

12.1. 向量字段

向量字段保存 Embedding 结果,是相似度检索的输入。常见的稠密浮点向量字段类型是 FLOAT_VECTOR。向量维度固定后,写入和查询都必须遵守同一个维度。

12.2. 标量字段

字符串、数字、布尔值等字段属于标量字段,可用于:

  • 过滤租户和部门;

  • 区分文档版本和发布状态;

  • 返回引用所需的业务 ID;

  • 关联 PostgreSQL 中的完整元数据。

不要把所有业务字段都无选择地复制到 Milvus。只把检索过滤和结果回填真正需要的字段放进去,其余数据由业务数据库负责。

12.3. 动态字段要谨慎使用

动态字段可以让数据结构更灵活,但也会降低 Schema 的可见性和约束能力。企业知识库通常更适合显式定义核心字段,把可选属性放入受控的 JSON 或业务数据库中。

十三、Partition:集合内部的逻辑分组

Partition 用于把一个 Collection 中的数据按逻辑划分。检索时可以指定某些 Partition,从而缩小搜索范围。

例如:

复制代码
knowledge_chunks
├── tenant_a
├── tenant_b
└── tenant_c

不过,不能简单地把每个租户、每个用户都创建成一个 Partition。租户数量不断增长时,Partition 数量也会膨胀,管理、加载和查询都会变复杂。

更常见的设计是:

  • Partition 数量有限且稳定:按业务域、数据冷热或时间范围划分;

  • 租户数量很多:使用 tenant_id 标量字段过滤;

  • 数据量特别大:结合分片和查询节点设计,而不是只依赖 Partition。

十四、Partition Key 与手工 Partition 有什么不同?

手工 Partition 需要应用自己决定数据写入哪个分区;Partition Key 则可以让系统根据字段值将数据路由到对应分区。二者适合的场景不同,不能只因为"分区看起来更快"就盲目使用。

如果业务过滤条件始终包含租户编号,租户字段可能适合作为分区键;如果查询经常跨租户,强行按租户划分反而可能造成大量分区访问。

十五、如何设计一条知识库记录?

推荐把 Milvus 记录设计成"检索索引",而不是完整业务主表:

复制代码
{
  "chunk_id": "doc-001:chunk-0007",
  "document_id": "doc-001",
  "tenant_id": "tenant-a",
  "status": "published",
  "embedding": [0.012, -0.084, 0.221]
}

检索命中后,再由 document_id、chunk_id 查询 PostgreSQL 中的标题、版本、权限和来源信息。这样可以避免在向量库中复制大量易变业务字段。

MinIO 中应该放什么?

Milvus 会把持久化数据、索引文件以及部分中间结果写入对象存储。这里的对象存储可以是官方示例中的 MinIO,也可以是兼容 S3 的云存储。它和 PostgreSQL 中的业务记录、RAG 应用保存原始 PDF 的对象存储不是同一层职责:

复制代码
原始文件:应用对象存储 / MinIO / OSS
业务元数据:PostgreSQL
向量与索引:Milvus 管理,底层持久化到 Milvus 配置的 MinIO / S3

如果把 Milvus 的对象存储目录误删,etcd 中可能仍然保留集合和索引元信息,但底层文件已经不可读。因此,备份 Milvus 时不能只备份 PostgreSQL,也不能只导出业务表,还要按照部署方案备份对象存储、元数据和配置,并在测试环境验证恢复流程。

十六、常见设计错误

16.1 错误一:不同模型的向量放在同一个字段

不同 Embedding 模型的维度、语义空间和归一化方式可能不同。模型升级时应新建向量字段或新 Collection,并进行对照评测。

16.2 错误二:把权限判断交给 Partition

Partition 是数据组织和检索范围机制,不是完整的授权系统。权限变化后,必须由业务服务重新计算用户可访问范围。

16.3 错误三:把文本全文都当成向量库主数据

文本回填、版本管理和审计通常由 PostgreSQL 或对象存储负责。Milvus 中的文本字段只保留满足检索结果回填需要的内容。

16.4 错误四:一开始创建大量分区

先根据查询模式和数据规模设计,必要时用标量过滤替代分区。分区不是越多越好。

十七、设计检查清单

  • Collection 是否只承载一种明确的向量检索任务;

  • 主键是否稳定且能关联业务记录;

  • 向量维度是否与模型固定对应;

  • 标量字段是否覆盖必要的租户、版本和状态过滤;

  • 是否真的需要手工 Partition;

  • 权限判断是否仍由业务服务负责;

  • 模型升级是否有重建和灰度方案。

  • 是否区分了 Milvus 的内部对象存储与应用原始文件存储;

  • 是否通过 Milvus API 管理集合,而不是直接修改 etcd;

  • 是否为 etcd、MinIO 和 Milvus 数据卷设计了备份与恢复方案。

十八、结语

18.1 Milvus 可以理解为 RAG 系统中的"专用向量检索层":它让向量存储、索引和大规模相似度搜索拥有独立的工程能力,但它并不替代模型、业务数据库和知识库处理流程。

18.2 Collection 负责组织一类向量实体,Field 负责描述记录结构,Partition 负责在集合内部进行逻辑划分。理解三者关系后,Milvus 的建模就不再是"照着示例复制 Schema",而是可以从检索模式、权限边界和数据生命周期反推设计。

参考资料

  1. Milvus 官方文档:What is Milvus

  2. Milvus 官方文档:Architecture Overview

  3. Milvus 官方文档:Run Milvus with Docker Compose

  4. Milvus 官方文档:Vector Search

  5. Milvus 官方文档:Schema

  6. Milvus 官方文档:Manage Collections

  7. Milvus 官方文档:Architecture Overview

  8. Milvus 官方文档:Partitions

16.Milvus 的 Collection、Partition、向量字段分别是什么?

相关推荐
燕落南枝1 小时前
HTML 视频标签(video)详解笔记
笔记·html·音视频
自信150413057591 小时前
重生之从0开始学习c++之string(下)
c++·学习·stl
kmomo..1 小时前
Linux 驱动开发学习笔记:UART、触摸屏与 ADC 驱动实战
linux·驱动开发·学习
youdexiang2 小时前
如何建立个人学习资料库?
学习
人工智能培训2 小时前
具身智能环境交互与学习范式原理
大数据·linux·人工智能·学习·算法·机器学习
Tj8105232 小时前
企业培训如何真正落地:从组织能力建设到培训ROI评估的完整指南
学习·学习方法
~kiss~2 小时前
LangGraph 和 ToT,Tree of Thoughts 同一步骤多条路线并行探索,用LLM做评判和减枝
学习
tz_zs2 小时前
【AI编程】使用笔记:Rule/Skill/Prompt
笔记·prompt·ai编程
深圳老胡2 小时前
STM32C5 系列 MCU 的 FreeRTOS 软件架构搭建指南:外设配置与应用实践
笔记·stm32·单片机·代码规范