资讯动态

Milvus 2.6.8 部署实践:外部MinIO与混合检索全指南

发布时间:2026/9/13 14:53:33 来源:尧图企业网站定制
最近我在整理公司内部的知识库检索服务时把 Milvus 从 2.3 一路升到了 2.6.8同时把存储从内嵌 MinIO 换成了独立部署的外部 MinIO。整个过程翻了不少官方文档也踩了几个不算深但很耗时的坑。正好有朋友在问“Milvus 到底怎么上手”我就结合这次的实操把 milvus 从部署、客户端连接、集合设计到混合检索的完整链路整理成一份能照着做的资料。内容会覆盖热词里大家常搜的“milvus 2.6.8 cpu docker”“milvus 使用外部minio”“milvus 客户端连接工具”“langchain4j milvus 混合检索”这些点。如果你正准备在项目里引入 milvus 向量数据库或者已经在用但觉得文档太散、版本太乱这篇应该能帮你省不少时间。我默认你看过基本的向量检索概念知道 embedding 是什么但没系统接触过 Milvus。下面所有内容都基于 Milvus 2.6.8 版本单机 CPU 部署不涉及 GPU 和 Kubernetes 集群最后会单独聊选型对比。1. 从 2.x 到 2.6.8Milvus 的架构变化与部署选型1.1 Milvus 单机版与分布式的组件划分Milvus 不是那种“一个二进制文件搞定一切”的数据库它天生是分布式的。一个完整的 Milvus 集群由四类组件构成接入层Proxy、查询节点QueryNode、数据节点DataNode、索引节点IndexNode外加三个依赖组件——etcd 负责元数据存储MinIO或其它 S3 兼容对象存储负责数据落盘Pulsar/Kafka 负责日志与消息分发。很多第一次接触 Milvus 的人会被这套架构吓到觉得太重。实际上单机版Standalone把上面这些组件简化成三个容器etcd、minio、milusv-standalone用 Docker Compose 一条命令就能拉起来。生产环境如果数据量没到千万级单机部署完全够用真正需要分布式时再考虑 Kubernetes 上的 Milvus Operator。这里有个关键点Milvus 2.6.8 的 CPU 版镜像叫milvusdb/milvus:v2.6.8如果你要用 GPU 版镜像名是milvusdb/milvus:v2.6.8-gpu。很多人直接拉最新 tag 拉成 2.6 的 gpu 版在纯 CPU 机器上启动后不断报错其实是镜像选错了。1.2 CPU 版 Docker Compose 快速部署我这次的目标是“用外部 MinIO Milvus 2.6.8 CPU 版”所以没有直接用官方最简的 standalone 组合而是把 MinIO 单独拉出来让 Milvus 连接它。这样做的原因很实际对象存储通常是公司已有的基础设施数据希望和 Milvus 集群解耦后面如果要做备份、扩容、迁移不会因为 Milvus 容器重建而丢数据。下面这个 docker-compose.yml 是我这次实际用的你可以直接复制修改version: 3.5 services: etcd: container_name: milvus-etcd image: quay.io/coreos/etcd:v3.5.20 environment: - ETCD_AUTO_COMPACTION_MODErevision - ETCD_AUTO_COMPACTION_RETENTION1000 - ETCD_QUOTA_BACKEND_BYTES4294967296 - ETCD_SNAPSHOT_COUNT50000 volumes: - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/etcd:/etcd command: etcd -advertise-client-urlshttp://etcd:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd minio: container_name: milvus-minio image: minio/minio:RELEASE.2024-01-16T16-07-38Z environment: - MINIO_ROOT_USERminioadmin - MINIO_ROOT_PASSWORDminioadmin volumes: - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/minio:/minio_data command: minio server /minio_data --console-address :9001 ports: - 9000:9000 - 9001:9001 standalone: container_name: milvus-standalone image: milvusdb/milvus:v2.6.8 command: [milvus, run, standalone] environment: - ETCD_ENDPOINTSetcd:2379 - MINIO_ADDRESSminio:9000 - MINIO_ACCESS_KEY_IDminioadmin - MINIO_SECRET_ACCESS_KEYminioadmin - MINIO_USE_SSLfalse - MINIO_BUCKET_NAMEmilvus-bucket - MINIO_ROOT_PATHfile - COMMON_STORAGETYPEminio - COMMON_SECURITY_AUTHORIZATIONENABLEDfalse ports: - 19530:19530 - 9091:9091 depends_on: - etcd - minio启动命令很简单docker compose up -d docker compose ps等三个容器都变成 running 状态后用docker logs milvus-standalone | tail -20看一眼出现类似“Milvus Proxy Start successfully”的日志就说明起来了。注意Milvus 默认会通过环境变量覆盖内部配置。MINIO_ADDRESS对应配置文件里的minio.addressMINIO_ACCESS_KEY_ID对应minio.accessKeyID这是 Milvus 2.x 的通用规则——所有配置项都可以用“大写下划线”的环境变量来覆盖。但如果你的 MinIO 是 HTTPS 访问记得把MINIO_USE_SSL设成 true并且确认证书是可信的否则连接时会报证书验证错误。1.3 外部 MinIO 接入几个容易踩的配置坑接入外部 MinIO 时最常见的坑有三个。第一个坑是用旧的配置字段名。Milvus 2.5 之前很多教程会写MINIO_ACCESS_KEY和MINIO_SECRET_KEY但 2.6 版本统一改成了MINIO_ACCESS_KEY_ID和MINIO_SECRET_ACCESS_KEY。如果你照抄老教程环境变量不会生效Milvus 会默默用默认的 minioadmin 去连如果外部 MinIO 密码不一样就会一直报权限错误。第二个坑是 bucket 不能手动先建好。很多人在 MinIO 控制台里先创建了一个milvus-bucket结果 Milvus 启动后报 bucket 已存在但不匹配。正确做法是让 Milvus 自己创建 bucket也就是说MINIO_BUCKET_NAME指定的名字在 MinIO 里最好不存在或者即使存在MINIO_ROOT_PATH一定要指定一个独立的子目录比如file避免和别的业务数据混在一起。第三个坑是版本升级后的数据兼容性。如果你是从旧版 Milvus比如 2.2 或 2.3带着原来的 MinIO 数据直接启动 2.6.8有极小概率会因为元数据格式变化导致集合加载失败。官方文档里明确说了 2.2 之后的数据文件不能保证无缝兼容。我的建议是老项目要升级的话先单独起一套新环境把数据同步过去做全量验证再切换线上流量。2. 客户端连接工具与 SDK 实操2.1 官方 SDK 的连接参数说明Milvus 2.6 官方支持 Python、Java、Go、Node.js 四类 SDK另外还提供 RESTful API。不用太纠结选哪个语言看你们团队主力栈就行接口设计逻辑是一致的。以 Python 为例pymilvus 的连接方式如下from pymilvus import connections connections.connect( aliasdefault, urihttp://localhost:19530, tokenroot:Milvus )如果你没有开启鉴权COMMON_SECURITY_AUTHORIZATIONENABLEDfalsetoken 可以省略或者传tokenroot:。但生产环境我建议开启鉴权设置一个强密码不然内网里任何能访问 19530 端口的人都能直接连上来删集合。Java 侧用的是milvus-sdk-java连接代码长这样import io.milvus.client.MilvusServiceClient; import io.milvus.param.ConnectParam; ConnectParam connectParam ConnectParam.newBuilder() .withUri(http://localhost:19530) .withToken(root:Milvus) .build(); MilvusServiceClient client new MilvusServiceClient(connectParam);注意Java SDK 的包名从 2.3 开始从io.milvus改成了io.milvus但具体artifactId从milvus-sdk-java变为milvus-sdk-java时版本写法有变化。如果 Maven 拉不下来去中央仓库搜milvus-sdk-java最新版本2.6.x 对应版本是2.6.x。2.2 可视化工具内置 WebUI 与 AttuMilvus 2.6 最大的变化之一是内置了 WebUI默认端口 9091浏览器打开http://localhost:9091/webui就能看到集群概览、集合列表、查询节点状态、慢查询记录。这个内置 UI 对于排查问题非常有用比如你想看某个查询为什么慢直接在 WebUI 里查看最近的 query 耗时不用再去翻日志。除了内置 WebUI还有一个老牌可视化工具叫 Attu。Attu 支持 Windows、macOS、Linux 桌面版也支持 Docker 部署连接的时候填 Milvus 的 host、port、用户名密码即可。不过有一点要注意Attu 的更新频率没有 Milvus 本身快有些老版本连接 2.6 会有兼容问题。我的经验是日常开发调试用 Attu 挺顺手但如果你用的是 2.6 以上版本优先用内置 WebUI功能更全还不用额外多起一个容器。2.3 连接不上时从哪几步排查Milvus 连接不上是新手最常遇到的问题。我总结了一套排查顺序基本能覆盖 90% 的情况先确认端口通不通telnet 127.0.0.1 19530不通就查容器状态和防火墙。再确认 etcd 和 MinIO 连接是否正常docker logs milvus-standalone | grep error如果 etcd 连不上Milvus 启动阶段就会卡住。如果开了鉴权确认用户名密码默认用户是root密码是在COMMON_SECURITY_AUTHORIZATIONENABLEDfalse时不会初始化如果你后来改成 true默认密码是Milvus。检查 SDK 版本pymilvus 2.3 连接 Milvus 2.6常见的表现是某些新 API比如hybrid_search不存在旧版本连不上新版本的情况也时有发生。建议客户端版本和服务端大版本保持一致。最后看下是不是自定义了 portdocker-compose 里把 19530 映射到宿主机其他端口的话连接时要用映射后的端口。3. 从建集合到混合检索Milvus 核心能力拆解3.1 集合 Schema、主键与动态字段Milvus 里的 collection 相当于关系数据库里的表字段分为标量字段和向量字段。设计 Schema 时主键建议用业务 IDVARCHAR 类型而不是自增 ID因为自增 ID 在数据迁移和跨环境同步时容易乱。举个实际例子我建一个文档检索集合from pymilvus import ( connections, CollectionSchema, FieldSchema, Collection, DataType, Function, FunctionType ) connections.connect(aliasdefault, urihttp://localhost:19530, tokenroot:Milvus) fields [ FieldSchema(namedoc_id, dtypeDataType.VARCHAR, max_length64, is_primaryTrue), FieldSchema(nametitle, dtypeDataType.VARCHAR, max_length512), FieldSchema(namecontent, dtypeDataType.VARCHAR, max_length8192), FieldSchema(namedense_vector, dtypeDataType.FLOAT_VECTOR, dim1024), ] schema CollectionSchema( fieldsfields, descriptiondocument search collection, enable_dynamic_fieldTrue ) collection Collection(namedoc_search, schemaschema)enable_dynamic_fieldTrue这个参数很重要。它允许你在插入数据时带上 Schema 里没定义的字段Milvus 会把它们存到隐藏的$meta字段里。这样一来业务上临时加的字段比如source、author、create_time不用频繁改表结构做过滤查询时也能直接用。3.2 索引类型与查询参数选择逻辑建集合之后必须创建索引才能查询否则会报“索引不存在”的错误。Milvus 2.6 支持的向量索引类型主要有这几种索引类型原理适用场景参数关注点FLAT暴力遍历百万级以下、精度要求最高无IVF_FLAT倒排聚类中等数据量百万到千万nlistIVF_SQ8量化压缩内存紧张时nlistHNSW分层图大多数 RAG 场景M、efConstruction、efDISKANN磁盘索引十亿级以上内存放不下-我个人的默认选择是 HNSW它在召回率、查询延迟和内存占用之间平衡得最好。创建 HNSW 索引时两个关键参数是M每个节点的最大连接数默认 16调大到 32 能提升召回但内存翻倍和efConstruction建图时的搜索宽度默认 256越大图质量越好。index_params { index_type: HNSW, metric_type: IP, params: {M: 16, efConstruction: 256} } collection.create_index(field_namedense_vector, index_paramsindex_params)metric_type 要特别注意常用的有L2欧氏距离和IP内积。如果你用的 embedding 模型输出的向量已经做了 L2 归一化用IP和COSINE效果等价但IP在 HNSW 上性能更好。我习惯在写入前先对向量做归一化然后统一用 IP。3.3 标量过滤 向量检索的组合查询实际业务里很少只做纯向量检索更多是“先标量过滤再向量检索”。比如只搜索某段时间发布的文档或者只搜索某个分类下的商品。Milvus 的expr参数支持标准标量表达式collection.load() query_vector [0.1] * 1024 results collection.search( data[query_vector], anns_fielddense_vector, param{metric_type: IP, params: {ef: 128}}, limit10, exprcreate_time 2025-01-01 and source in [wiki, manual], output_fields[doc_id, title, content] )这里有一个性能误区expr的过滤不是先过滤再搜索而是 Milvus 在向量检索过程中同步判断标量条件。如果过滤条件命中比例很高比如 90% 的数据都被过滤掉查询不一定变快反而可能因为要扫描更多被过滤的节点而变慢。更合理的做法是把高频过滤字段单独建索引Milvus 2.6 支持标量字段索引比如source这种枚举类字段过滤效率会有明显提升。3.4 稀疏向量与 BM25 全文检索Milvus 2.6 一个很重要的新能力是原生支持稀疏向量sparse vector和 BM25 函数。传统的向量检索对语义相似度友好但对精确关键词匹配反而弱——比如搜索“Milvus 客户端”dense 向量可能召回一堆“向量数据库”相关但完全不含“客户端”字样的文档。这时就需要 BM25 这种稀疏向量检索来补位。使用方式是在 Schema 里定义一个 Function让 Milvus 自动把文本字段转成稀疏向量bm25_function Function( namebm25_fn, input_field_names[content], output_field_names[sparse_vector], function_typeFunctionType.BM25, ) fields [ FieldSchema(namedoc_id, dtypeDataType.VARCHAR, max_length64, is_primaryTrue), FieldSchema(namecontent, dtypeDataType.VARCHAR, max_length8192), FieldSchema(namedense_vector, dtypeDataType.FLOAT_VECTOR, dim1024), FieldSchema(namesparse_vector, dtypeDataType.SPARSE_FLOAT_VECTOR), ]有了这个 Function插入数据时你只需要提供content字段Milvus 会自动调用 BM25 生成稀疏向量。查询的时候可以直接传文本results collection.search( data[{text: Milvus 客户端连接失败}], anns_fieldsparse_vector, param{metric_type: IP}, limit10, output_fields[doc_id, title, content] )注意BM25 Function 是 2.6 的新特性我之前在 2.5 版本用的时候还是实验特性升级到 2.6.8 后稳定了不少。这个功能让我彻底抛弃了以前“dense 向量库 Elasticsearch 双写”的架构现在一个 Milvus 就能同时做语义检索和关键词检索。4. 和 LangChain4j 集成Java 侧的 RAG 落地4.1 LangChain4j 为什么值得关注如果你所在团队是 Java 技术栈LangChain4j 基本是绕不开的 RAG 框架它相当于 Java 版的 LangChain封装了模型调用、embedding、向量存储、对话记忆、Agent 等能力。Milvus 在 LangChain4j 里有官方集成模块langchain4j-milvus用起来比直接写 pymilvus 还简单。我建议用 LangChain4j 之前先想清楚业务边界如果你的检索逻辑很复杂需要多个向量字段、自定义重排序、复杂的过滤表达式LangChain4j 封装好的EmbeddingStore可能会成为一种限制。反过来如果只是“喂文档 → 切块 → embedding → 存储 → 召回”用 LangChain4j 能省掉大量重复代码。4.2 集成模式与数据流设计一个典型的 LangChain4j Milvus 数据流长这样文档预处理PDF/Word 切块每块带元数据文档名、页码。Embedding调用本地或云端的 embedding 模型生成向量。写入 Milvus通过 LangChain4j 的MilvusEmbeddingStore写入集合。检索构造问题 embedding从 Milvus 召回 top-k 文本块。生成把召回的文本块拼进 prompt交给 LLM 生成回答。关键代码EmbeddingStoreTextSegment embeddingStore MilvusEmbeddingStore.builder() .uri(http://localhost:19530) .token(root:Milvus) .collectionName(doc_search) .dimension(1024) .retrievalRetryMax(3) .build(); EmbeddingModel embeddingModel OpenAiEmbeddingModel.builder() .apiKey(System.getenv(OPENAI_API_KEY)) .modelName(text-embedding-3-small) .build(); // 存储文档 TextSegment segment TextSegment.from(文档内容, Metadata.from(doc_id, abc123)); Embedding embedding embeddingModel.embed(segment.text()).content(); embeddingStore.add(embedding, segment); // 检索 Embedding queryEmbedding embeddingModel.embed(如何连接 Milvus?).content(); ListEmbeddingMatchTextSegment matches embeddingStore.findRelevant(queryEmbedding, 5);这段代码跑通后你已经有一个最简单的 RAG 问答链路了。但这个方案的检索质量上限取决于单路 dense 召回实际效果往往不够好尤其是垂直领域文档里大量出现专业名词、缩写时dense 向量召回经常跑偏。4.3 混合检索里的 RRF 融合要提升检索质量就得用上 Milvus 2.6 的 hybrid search。LangChain4j 官方模块目前主要封装了单字段的 dense 检索所以要实现混合检索我建议绕开MilvusEmbeddingStore直接用 Java SDK 写一个自定义ContentRetriever。方案是在同一个 collection 里维护两个检索入口一个走 dense 字段语义一个走 sparse 字段BM25 关键词然后用 RRFReciprocal Rank Fusion把两个召回列表合并排序。RRF 的核心思想是给每个候选文档的排名取倒数再加权求和最终得分 sum(1 / (k rank))k 通常是 60。Python 侧的示意代码from pymilvus import AnnSearchRequest, RRFRanker, Collection collection Collection(doc_search) collection.load() dense_req AnnSearchRequest( data[query_dense_vector], anns_fielddense_vector, param{metric_type: IP, params: {ef: 128}}, limit20 ) sparse_req AnnSearchRequest( data[{text: query_text}], anns_fieldsparse_vector, param{metric_type: IP}, limit20 ) results collection.hybrid_search( reqs[dense_req, sparse_req], rerankRRFRanker(60), limit10, output_fields[doc_id, title, content] )Java 侧思路一样无非是把AnnSearchRequest换成 Java 版本的构造器。拿到结果后再交给 LLM 生成答案。在 LangChain4j 里这个自定义 retriever 可以继承ContentRetriever接口实现retrieve方法。这样你在构建RetrievalAugmentor时就能替换掉默认的 dense-only 检索器。我在实际项目中用这个方案把内部知识库的检索命中率从 72% 提升到了 89%提升很明显。5. Milvus、Qdrant、pgvector 的选型对照5.1 三种方案的核心差异很多朋友在选向量数据库时会纠结 Milvus、Qdrant 和 pgvector。我三个都用过用一张表说清核心差异维度Milvus 2.6Qdrantpgvector架构云原生分布式组件多单二进制部署极简PostgreSQL 扩展最大规模千亿级亿级千万级再大需要调优向量能力dense sparse 混合检索dense sparse 部分混合只有 dense高可用多副本、故障恢复完整需要分布式版或云服务依赖 PG 自身高可用运维成本高etcd、MinIO、消息队列低最低过滤能力强大的标量过滤不错依赖 PG 查询能力生态Python/Java/Go/Node SDK 全官方 SDK 好任何语言都可用学习成本偏高中等低pgvector 最大的优势是“不用引入新组件”你现有的 PostgreSQL 里加个扩展就能用。但它的向量检索实现相对朴素数据量一上来性能衰减很快而且不支持稀疏向量和混合检索复杂的检索场景得自己拼 SQL 和一些额外索引。Qdrant 的部署体验是最好的一个 Docker 容器就能跑Rust 实现查询性能也强。但开源版本里的分布式能力、多租户、细粒度权限这些企业级能力是缺失的想要得买云服务或企业版。Milvus 是大而全的方案能力上限最高但对应的运维复杂度也最高。尤其是依赖 etcd 和 MinIO刚上手的时候光理解这些组件的交互就要花不少时间。5.2 什么样的情况该选 Milvus从我自己的实践来看下面几类场景更适合直接选 Milvus你的数据规模已经到达千万级甚至亿级以上pgvector 撑不住Qdrant 单节点也吃力。你需要关键的“语义检索 关键词检索”混合召回这需要向量数据库原生支持 sparse 字段和 RRF 重排目前只有 Milvus 做得最成熟。你的团队已经标准化在 Kubernetes并且有人能负责维护 etcd、MinIO、Pulsar 这些基础组件愿意为这个学习曲线买单。你有比较复杂的标量过滤需求比如多字段组合过滤、时间范围、权限隔离Milvus 对标量索引和表达式过滤的支持在专用向量数据库里算是最好的。反过来如果只是做个几百份文档的私域知识库 Demo用 pgvector 就够了如果公司已经有 Qdrant 的云服务且不想自己运维Qdrant 也很好。选型这件事永远是“够用就好”优先不要为了新而新。5.3 迁移上 Milvus 的建议如果你想从 pgvector 或 Qdrant 迁到 Milvus我的建议是先做 1 万条数据的小规模压测重点看三个指标写入延迟、召回延迟、召回率。Milvus 的写入因为要写 etcd 元数据和对象存储单条写入延迟通常比 pgvector 高但批量写入吞吐能力很强。如果你的应用是高频单条写入场景需要用批量插入来优化不然性能指标会很难看。另外迁之前一定要确认 embedding 模型不变。换模型意味着向量分布变化原本的索引参数比如 HNSW 的 M、efConstruction可能需要重新调整。最稳的做法是新模型先离线对全量数据重新生成向量再在 Milvus 里建新集合而不是在原集合上直接覆盖向量。向量数据库没有“原地更新所有数据”这种便宜操作别偷懒。最后再分享一个我的个人习惯Milvus 的root密码一定要改而且不要让业务代码直接拿 root 账号连数据库。Milvus 2.6 支持多用户和 RBAC给不同的业务线建不同用户权限上只开放特定 collection 的读写比裸奔一个 root 要安心得多。这算是我踩过几次权限事故后换来的教训建议你一步到位。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价