AI全栈知识07:向量数据库 - Milvus/Chroma实战

写在前面
上一篇我们知道了RAG需要"向量检索"来搜相关文档。那向量存在哪?怎么搜?为什么不能用MySQL?
这篇帮你搞清楚向量数据库是什么、怎么选、怎么用。学完后你既能用Chroma快速开发,也知道生产环境Milvus是怎么回事。
为什么MySQL干不了这个活
存可以,搜不动。
假设你有100万段文档,每段转成1536个数字(向量)。存MySQL没问题,100万行1536列。
但搜索时出问题了:
用户问了一个问题,转成向量 [0.12, -0.34, 0.78, ...]
MySQL要找最相似的5段:
第1步:把100万行全部取出来
第2步:逐行计算余弦相似度(100万次浮点运算)
第3步:排序取前5
结果:耗时几秒到几十秒
向量数据库有专门的索引,不需要逐行比对。就像MySQL有B+树能快速定位某一行,向量数据库有HNSW索引能快速定位"附近的向量":
同样100万条:
MySQL暴力搜索:3-10秒
向量数据库(有索引):5-20毫秒
差了几百倍。

生活类比
| 需求 | MySQL的做法 | 向量数据库的做法 |
|---|---|---|
| "找一本叫《K8s权威指南》的书" | 在目录里按书名精确查找,秒出结果 | 不擅长这个 |
| "找跟容器故障排查相关的书" | 不会。只能搜标题里有没有这几个字 | 理解语义,把相关的都找出来 |
MySQL像图书馆的电子目录 (按编号精确定位)。
向量数据库像一个懂内容的图书管理员(按你的意思推荐相关的)。
两者不是替代关系,是各干各的活。
Chroma - 最简单的向量数据库
为什么推荐先学它
一个原因:pip install就能用,不需要装数据库服务。
就像SQLite不需要装MySQL服务一样,Chroma直接在Python代码里跑,数据存本地文件。开发测试最方便。
完整代码示例
python
# 安装:pip install chromadb
import chromadb
# 1. 创建客户端(数据存本地)
client = chromadb.Client()
# 2. 创建集合(类似MySQL的"表")
collection = client.create_collection("ops_docs")
# 3. 添加文档(Chroma自动把文字转成向量)
collection.add(
documents=[
"当容器内存超过limit时,K8s会触发OOMKilled终止Pod",
"Pod处于Pending状态通常是因为资源不足或调度约束不满足",
"使用kubectl describe pod可以查看Pod的Events信息",
"K8s的HPA根据CPU或自定义指标自动扩缩Pod数量",
"Ingress是K8s中管理外部HTTP流量进入集群的资源",
],
ids=["doc1", "doc2", "doc3", "doc4", "doc5"]
)
# 4. 语义搜索
results = collection.query(
query_texts=["为什么Pod一直重启"], # 用户的问题
n_results=2 # 返回最相似的2条
)
print(results["documents"])
# 输出:[["当容器内存超过limit时...OOMKilled", "使用kubectl describe pod..."]]
就这么几行。 存文档、自动转向量、语义搜索全搞定。
Chroma做了什么
你调collection.add()时,Chroma在背后做了:
- 调内置的Embedding模型把文字转成向量
- 建立索引
- 存到本地文件
你调collection.query()时:
- 把你的问题也转成向量
- 用索引快速找到最近的向量
- 返回对应的原始文档
对你来说只需要关心:存什么文档、搜什么问题、要几条结果。 中间的向量转换和检索过程全自动。
Milvus - 生产级向量数据库
什么时候需要Milvus
| 需求 | Chroma够吗 | 需要Milvus |
|---|---|---|
| 几千条文档,开发测试 | 够 | 不需要 |
| 几十万条文档,多人使用 | 勉强 | 建议用 |
| 百万级以上,生产环境 | 不行 | 必须用 |
| 需要高可用/集群部署 | 不支持 | 支持 |
| 需要持久化和备份 | 简陋 | 完善 |
你们公司的FastGPT背后大概率就是Milvus(或pgvector)在存知识库的向量。
Milvus的架构
Milvus集群 = 三个组件配合:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
| Milvus | | etcd | | MinIO |
| (主程序) | | (元数据) | | (文件存储) |
| 向量索引 | | 集合信息 | | 向量数据 |
| 查询处理 | | Schema等 | | 索引文件 |
└─────────────┘ └─────────────┘ └─────────────┘
| 组件 | 作用 | 类比 |
|---|---|---|
| Milvus | 处理查询和索引 | MySQL主进程 |
| etcd | 存元数据(有哪些集合、字段定义) | MySQL的information_schema |
| MinIO | 存实际的向量数据文件 | MySQL的数据目录 |
Docker部署
yaml
# docker-compose.yml(最简单的单机部署)
version: '3.5'
services:
milvus-standalone:
image: milvusdb/milvus:v2.4.0
ports:
- "19530:19530"
- "9091:9091"
volumes:
- ./milvus-data:/var/lib/milvus
environment:
ETCD_ENDPOINTS: etcd:2379
MINIO_ADDRESS: minio:9000
depends_on:
- etcd
- minio
etcd:
image: quay.io/coreos/etcd:v3.5.5
environment:
ETCD_AUTO_COMPACTION_MODE: revision
ETCD_AUTO_COMPACTION_RETENTION: "1000"
minio:
image: minio/minio:RELEASE.2023-03-20T20-16-18Z
environment:
MINIO_ACCESS_KEY: minioadmin
MINIO_SECRET_KEY: minioadmin
command: minio server /minio_data
bash
docker-compose up -d
# 三个容器启动,Milvus就绪
Python使用示例
python
from pymilvus import connections, Collection, FieldSchema, CollectionSchema, DataType
# 连接Milvus
connections.connect(host="localhost", port="19530")
# 定义表结构(Schema)
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True),
FieldSchema(name="text", dtype=DataType.VARCHAR, max_length=2000),
FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=1536),
]
schema = CollectionSchema(fields, description="运维知识库")
# 创建集合
collection = Collection("ops_knowledge", schema)
# 创建索引(很重要!没索引会很慢)
collection.create_index(
field_name="embedding",
index_params={
"index_type": "HNSW", # 索引类型
"metric_type": "COSINE", # 相似度算法
"params": {"M": 16, "efConstruction": 256}
}
)
# 插入数据(需要自己调Embedding模型转向量)
collection.insert([texts, embeddings])
# 搜索
collection.load() # 加载到内存
results = collection.search(
data=[query_embedding],
anns_field="embedding",
param={"metric_type": "COSINE", "params": {"ef": 64}},
limit=5,
output_fields=["text"] # 同时返回原文
)
注意和Chroma的区别: Milvus不会自动调Embedding,你需要自己先把文字转成向量再存进去。
Chroma vs Milvus对比
| 维度 | Chroma | Milvus |
|---|---|---|
| 定位 | 开发/学习/小规模 | 生产/大规模 |
| 安装 | pip install chromadb |
Docker部署(3个容器) |
| 数据量 | 几万到几十万 | 亿级 |
| 搜索速度 | 毫秒级(小数据时) | 毫秒级(大数据也快) |
| 自动Embedding | 支持(内置模型) | 不支持(自己调) |
| 持久化 | 本地文件 | MinIO分布式存储 |
| 高可用 | 不支持 | 支持集群 |
| 监控 | 无 | 有Prometheus指标 |
| 适合 | K8sChat开发、学习 | FastGPT生产环境 |

索引类型(了解即可)
向量数据库的索引决定了搜索速度和精度:
| 索引类型 | 原理(类比) | 特点 |
|---|---|---|
| FLAT | 挨个对比(像翻字典每一页) | 最精确但最慢 |
| IVF | 先分区再搜(先找到对的书架再找书) | 速度快,有少量精度损失 |
| HNSW | 跳跃式搜索(像"六度人脉",通过朋友的朋友快速找到人) | 速度极快,内存占用大 |
结论:大多数情况选HNSW就对了。
运维Milvus要关心什么
如果你的工作中需要运维Milvus集群:
| 关注点 | 说明 | 监控方式 |
|---|---|---|
| 磁盘空间 | 100万条x1536维 约 6GB | 看MinIO存储 |
| 内存使用 | HNSW索引全部加载到内存 | 看Milvus内存 |
| 查询延迟 | 正常应该在5-20ms | Prometheus指标 |
| 集合大小 | 单集合不建议超过1亿条 | 定期检查 |
| 数据备份 | 备份MinIO中的数据 | 定时任务 |
| etcd健康 | etcd挂了Milvus不可用 | etcd健康检查 |
面试怎么说
如果被问"你了解向量数据库吗":
"了解。向量数据库专门用于存储和检索高维向量,核心能力是做语义相似度搜索。跟MySQL的区别是,MySQL做精确匹配(WHERE=),向量数据库做相似度匹配(找最像的前K条)。
我用过Chroma做开发(pip install就能用),了解Milvus的生产部署(etcd+MinIO+Milvus三组件)。索引一般选HNSW,相似度用余弦。
我们公司的FastGPT知识库功能底层就是向量数据库在支撑检索。"
延伸思考
| 问题 | 答案 |
|---|---|
| pgvector能替代Milvus吗? | 小规模可以(几十万条以下),大规模性能不如专用向量数据库 |
| 向量数据库会不会被MySQL替代? | 短期不会。MySQL加向量功能了但性能差距大 |
| Milvus和Elasticsearch能混用吗? | 可以。ES做关键词搜索+Milvus做向量搜索,两路结果合并(混合检索) |
| 向量数据会不会很大? | 100万条x1536维约6GB,不算特别大。但索引加载到内存会占更多 |
小结
本篇核心收获:
- MySQL做不了向量检索(暴力搜太慢),向量数据库有专用索引(HNSW)
- Chroma:pip install就能用,开发首选
- Milvus:生产级,Docker部署三组件(Milvus+etcd+MinIO)
- Chroma自动转向量,Milvus需要自己调Embedding
- 索引选HNSW,相似度用余弦,大多数场景最佳
- 运维Milvus关注:内存(索引加载)、磁盘(数据存储)、延迟(5-20ms正常)
下一篇预告
AI全栈知识08:Function Calling与工具调用
下一篇我们将深入Agent的核心机制:
- Function Calling到底是什么
- 模型怎么"决定"调哪个工具
- 完整的调用流程拆解
- K8sChat项目中Function Calling的实际应用