ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

向量数据库选型与实战:Docker部署Milvus及Python客户端操作指南

向量数据库选型与实战:Docker部署Milvus及Python客户端操作指南

1. 为什么我最终选了 Milvus 而不是 Chroma 或 Qdrant

向量数据库这两年火得不行,尤其是做 RAG 知识库、语义搜索、推荐系统的朋友,几乎绕不开选型这个话题。我最早用的是 Chroma,轻量、上手快,几行 Python 代码就能跑起来,做原型验证确实舒服。但项目一旦进入真实场景,数据量从几万条涨到几百万条,Chroma 的查询延迟就开始肉眼可见地往上飙,而且它原生不支持分布式部署,横向扩展基本靠手动分片,运维成本陡增。后来我又试了 Qdrant,Rust 写的,性能确实猛,单机场景下表现非常亮眼,但它的生态和社区文档相比 Milvus 还是薄了一些,遇到冷门问题搜半天找不到答案。

最终让我下定决心切到 Milvus 的原因有三个。第一,Milvus 是专门为大规模向量检索设计的,从底层存储到索引结构都是围绕这个目标做的,支持的数据量和并发能力上限明显更高。第二,它的索引类型非常丰富,IVF_FLAT、IVF_SQ8、HNSW、DiskANN 等等,不同场景可以灵活选择,不像有些轻量方案只给你一两种选项。第三,社区活跃度和文档完善程度在开源向量数据库里属于第一梯队,遇到问题基本都能找到参考。

当然,Milvus 的部署复杂度也比 Chroma 高不少,这也是为什么我选择用 Docker 来跑——把环境依赖全部封装在容器里,省去了手动编译、配置依赖的麻烦。这篇文章就把我从零开始在 Docker 里安装配置 Milvus、再到基本使用的完整过程梳理一遍,包括踩过的坑和实测有效的参数配置。如果你也在做知识库、语义检索相关的项目,或者单纯想了解一下向量数据库怎么落地,这篇内容应该能帮你省下不少折腾的时间。

2. 环境准备与 Docker 安装确认

2.1 硬件与系统的最低要求

Milvus 对机器资源是有一定要求的,不是随便一台笔记本就能跑得很顺畅。根据我的实测经验,单机版 Milvus 至少需要 4GB 内存和 2 核 CPU,这是能跑起来的下限。但如果你要处理百万级别的向量数据,建议至少 16GB 内存起步,CPU 核心数越多越好,因为向量索引构建阶段是计算密集型的。磁盘方面,Milvus 会把数据持久化到本地,SSD 的读写速度对查询性能影响很大,机械硬盘虽然能用,但查询延迟会明显增加。

操作系统这块,Linux 是最省心的选择,Ubuntu 20.04 及以上、CentOS 7 及以上都没问题。macOS 也能跑,但要注意 Docker Desktop 的资源分配。Windows 用户需要先装 Docker Desktop,并且确保开启了 WSL2 后端,否则 Milvus 的某些依赖可能跑不起来。我自己是在 Ubuntu 22.04 上操作的,后面提到的命令和路径都以 Linux 环境为准,Windows 和 macOS 的差异我会单独说明。

2.2 Docker 与 Docker Compose 的安装检查

在装 Milvus 之前,先确认 Docker 环境是否就绪。打开终端,执行下面这两条命令:

docker --version docker compose version

如果都能正常输出版本号,说明环境没问题。如果提示command not found,那就需要先安装 Docker。Ubuntu 上的安装命令如下:

sudo apt update sudo apt install -y docker.io docker-compose-plugin sudo systemctl enable docker sudo systemctl start docker

安装完成后,建议把当前用户加入 docker 组,这样后续操作就不需要每次都加sudo:

sudo usermod -aG docker $USER

执行完这条命令后需要重新登录终端才会生效。这一步很多人会忽略,结果后面执行 docker 命令一直报权限错误,还以为是安装出了问题。

注意:如果你用的是 Windows 或 macOS,直接安装 Docker Desktop 即可,它自带 Docker Compose。Windows 用户务必确认 WSL2 已启用,否则 Docker Desktop 可能无法正常启动。

2.3 拉取 Milvus 镜像前的网络与磁盘检查

Milvus 的 Docker 镜像体积不小,单机版的核心镜像大概在 1GB 左右,加上 etcd 和 MinIO 的镜像,总共需要下载 2GB 以上的数据。所以在拉取镜像之前,先确认磁盘空间充足:

df -h

至少保证/var/lib/docker所在分区有 10GB 以上的可用空间。另外,国内拉取 Docker Hub 镜像的速度可能比较慢,可以配置镜像加速器。编辑/etc/docker/daemon.json文件:

{ "registry-mirrors": ["https://your-mirror-url.com"] }

然后重启 Docker 服务:

sudo systemctl daemon-reload sudo systemctl restart docker

这一步不是必须的,但能显著提升镜像拉取速度,节省等待时间。

3. Milvus 单机版 Docker 部署全流程

3.1 下载官方 docker-compose 配置文件

Milvus 官方推荐用 Docker Compose 来部署单机版,因为单机版实际上包含三个组件:Milvus 本体、etcd(负责元数据存储)、MinIO(负责对象存储)。用 Compose 可以把这三个组件的依赖关系和网络配置一次性管理好,比手动逐个启动容器省事得多。

官方提供了现成的配置文件,直接下载即可:

wget https://github.com/milvus-io/milvus/releases/download/v2.4.0/milvus-standalone-docker-compose.yml -O docker-compose.yml

这里我选的是 v2.4.0 版本,你也可以去 Milvus 的 GitHub Releases 页面看看有没有更新的稳定版。下载完成后,建议先打开文件看一眼内容,确认各个服务的配置是否符合你的预期:

cat docker-compose.yml

你会看到三个 service 定义:etcd、minio和standalone。每个 service 都定义了端口映射、数据卷挂载和环境变量。默认情况下,Milvus 对外暴露的端口是19530(gRPC 和 RESTful 共用),etcd 用 2379,MinIO 用 9000 和 9001。

3.2 启动 Milvus 服务与状态验证

配置文件准备好之后,在同一个目录下执行:

docker compose up -d

-d参数表示后台运行。第一次执行会先拉取镜像,根据网络情况可能需要几分钟到十几分钟不等。拉取完成后,容器会自动启动。用下面的命令查看运行状态:

docker compose ps

正常情况下你会看到三个容器都是running状态。如果某个容器显示exited或者restarting,那就需要查看日志排查问题:

docker compose logs standalone

这一步很关键,因为 Milvus 启动时会依赖 etcd 和 MinIO 先就绪,如果 etcd 启动失败,Milvus 本体也会跟着挂掉。我遇到过最常见的问题是端口冲突,比如本机已经装了 etcd 或者 Redis 占用了 2379 端口,导致 Milvus 自带的 etcd 起不来。解决办法要么是停掉本机已有的服务,要么是修改 docker-compose.yml 里的端口映射。

3.3 健康检查与端口连通性测试

容器都跑起来之后,还需要确认 Milvus 服务本身是否真正就绪。官方提供了一个健康检查接口:

curl http://localhost:9091/healthz

如果返回OK,说明 Milvus 已经可以正常接受请求了。注意这个 9091 端口是 Milvus 的监控和健康检查端口,和 19530 的业务端口不是一回事。

再验证一下 19530 端口是否在监听:

ss -tlnp | grep 19530

或者用telnet测试连通性:

telnet localhost 19530

如果连接成功,说明 Milvus 的 gRPC 服务已经就绪,接下来就可以用客户端连接了。

提示:如果你是在远程服务器上部署的 Milvus,需要确保防火墙放行了 19530 端口,否则本地客户端连不上。云服务器还要检查安全组规则。

4. Python 客户端连接与基础操作实战

4.1 安装 pymilvus 并建立连接

Milvus 提供了多种语言的客户端,Python 这边用的是pymilvus。安装很简单:

pip install pymilvus

建议用虚拟环境安装,避免和系统 Python 的包冲突。安装完成后,写一段代码测试连接:

from pymilvus import connections, utility connections.connect( alias="default", host="localhost", port="19530" ) print(utility.get_server_version())

如果能看到 Milvus 的版本号输出,说明连接成功。这里alias参数是给连接起个别名,后续操作多个 Milvus 实例时会用到,单实例场景下用default就行。

4.2 创建 Collection 与定义 Schema

Milvus 里的数据组织方式和传统数据库不太一样。它用Collection来类比关系型数据库里的表,用Field来类比列,但多了一个关键概念:向量字段。每个 Collection 必须有一个主键字段和一个向量字段,向量字段的维度必须和你的 embedding 模型输出维度一致。

下面是一个创建 Collection 的完整示例:

from pymilvus import ( connections, Collection, CollectionSchema, FieldSchema, DataType ) # 定义字段 fields = [ FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True), FieldSchema(name="title", dtype=DataType.VARCHAR, max_length=512), FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=768) ] # 定义 Schema schema = CollectionSchema(fields=fields, description="文档向量库") # 创建 Collection collection = Collection(name="documents", schema=schema)

这里有几个点需要注意。auto_id=True表示主键由 Milvus 自动生成,你不需要手动指定。VARCHAR类型必须指定max_length,否则会报错。向量维度dim=768是根据你的 embedding 模型来的,比如 BERT 系列通常是 768 维,OpenAI 的 text-embedding-ada-002 是 1536 维,一定要对应上,否则插入数据时会报维度不匹配的错误。

4.3 创建索引与加载 Collection

Collection 创建好之后还不能直接查询,必须先创建索引并加载到内存。索引的作用是加速向量检索,不同的索引类型适用于不同的场景:

索引类型适用场景内存占用查询速度召回率
FLAT小数据集,要求高召回高慢最高
IVF_FLAT中等数据集,平衡型中中高
IVF_SQ8大数据集,内存受限低快中高
HNSW高性能场景高最快高

我一般用 HNSW,因为它在查询速度和召回率之间平衡得最好。创建索引的代码如下:

index_params = { "metric_type": "COSINE", "index_type": "HNSW", "params": {"M": 16, "efConstruction": 200} } collection.create_index( field_name="embedding", index_params=index_params ) collection.load()

metric_type指定相似度度量方式,常用的有L2(欧氏距离)、IP(内积)、COSINE(余弦相似度)。做文本语义检索一般用COSINE,因为文本 embedding 通常已经归一化了,余弦相似度更能反映语义接近程度。M参数控制每个节点的最大连接数,越大索引越精确但内存占用也越高,16 是一个比较通用的值。efConstruction影响索引构建时的搜索范围,200 算是质量和速度的折中。

4.4 插入数据与向量检索

数据插入支持单条和批量两种方式,实际使用中一定要批量插入,单条插入的效率极低。下面是一个批量插入的例子:

import random # 模拟生成数据 titles = [f"文档标题_{i}" for i in range(1000)] embeddings = [[random.random() for _ in range(768)] for _ in range(1000)] # 批量插入 data = [titles, embeddings] collection.insert(data) # 刷新确保数据可查 collection.flush()

插入完成后,用flush()确保数据落盘。然后就可以做向量检索了:

search_params = {"metric_type": "COSINE", "params": {"ef": 64}} query_vector = [[random.random() for _ in range(768)]] results = collection.search( data=query_vector, anns_field="embedding", param=search_params, limit=5, output_fields=["title"] ) for hits in results: for hit in hits: print(f"ID: {hit.id}, 距离: {hit.distance}, 标题: {hit.entity.get('title')}")

ef参数控制搜索时的候选集大小,越大召回率越高但速度越慢,64 是一个比较常用的值。limit=5表示返回最相似的 5 条结果。output_fields指定除了向量之外还要返回哪些字段,这里我们返回了title。

5. 常见问题排查与性能调优经验

5.1 容器启动失败的典型原因

Milvus 容器启动失败的原因有很多,我整理了一个速查表:

现象可能原因解决方法
etcd 容器反复重启端口 2379 被占用修改端口映射或停掉冲突服务
standalone 容器启动后立即退出内存不足增加可用内存或调整 JVM 参数
MinIO 容器报错数据卷权限问题检查挂载目录的读写权限
健康检查一直不通过依赖服务未就绪等待 etcd 和 MinIO 完全启动后再试

最常见的就是端口冲突和内存不足。端口冲突用ss -tlnp就能查出来,内存不足的话看docker compose logs里的 OOM 相关日志。另外,如果你之前跑过旧版本的 Milvus,数据卷里可能残留了不兼容的元数据,建议先docker compose down -v清空数据卷再重新启动。

5.2 查询延迟过高的排查思路

查询延迟高通常有几个原因。第一是索引没建对,比如用 FLAT 索引去查百万级数据,那肯定慢。第二是Collection 没有 load,Milvus 查询前必须把 Collection 加载到内存,没加载的话会直接报错而不是慢。第三是ef 参数设得太大,虽然召回率高了,但搜索范围也大了,延迟自然上去。第四是硬件资源瓶颈,CPU 或内存不够,索引构建和查询都会受影响。

我的调优顺序一般是:先确认索引类型是否合适,再检查 ef 参数是否合理,最后看硬件资源使用率。用docker stats可以实时查看容器的 CPU 和内存占用情况,非常直观。

5.3 数据持久化与备份策略

Milvus 单机版的数据默认存在 Docker 数据卷里,具体路径是volumes/minio和volumes/etcd。如果你直接docker compose down,数据卷不会被删除,下次up的时候数据还在。但如果加了-v参数,数据卷就会被清空,所有数据丢失。

生产环境一定要做定期备份。最简单的办法是直接备份volumes目录:

tar -czvf milvus_backup_$(date +%Y%m%d).tar.gz volumes/

恢复的时候把压缩包解压回原目录,再重新启动容器即可。另外,Milvus 也支持通过 API 做数据导出,适合数据量特别大的场景。

注意:备份前最好先停掉 Milvus 服务,确保数据一致性。热备份虽然也能做,但存在数据损坏的风险。

6. 从单机到集群的扩展思路

单机版 Milvus 适合开发和中小规模生产环境,但如果你的数据量到了千万级别,或者并发查询量很大,就需要考虑集群版了。集群版和单机版的核心区别在于:单机版把 etcd、MinIO、Milvus 都放在一台机器上,集群版则把这些组件拆开,Milvus 本身也拆成多个角色节点,包括 Proxy、Query Node、Data Node、Index Node 等。

从单机迁移到集群,最大的变化是 docker-compose 配置文件会复杂很多,需要配置各个节点之间的通信地址和依赖关系。官方也提供了集群版的 docker-compose 模板,但说实话,生产环境更推荐用 Kubernetes 来部署,因为集群版的组件多,用 K8s 做编排和扩缩容会方便很多。

不过对于大多数个人项目和小团队来说,单机版已经足够用了。我自己的知识库项目跑了大概 50 万条向量数据,单机版 Milvus 在 16GB 内存的机器上查询延迟稳定在 10ms 以内,完全满足需求。等到数据量真的撑不住了再考虑集群也不迟,没必要一开始就上重型方案。

最后分享一个我在实际使用中总结的小技巧:Milvus 的 Collection 可以按业务维度拆分,比如文档库一个 Collection、图片库一个 Collection,而不是把所有向量都塞进同一个 Collection。这样做的好处是索引可以针对性优化,查询时也避免了不必要的跨领域干扰,召回质量会明显提升。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进