别再问我向量数据库是啥了,看完这篇你就全明白了
最近一两年,只要跟AI沾边的东西都火得一塌糊涂,什么大模型、RAG、Agent,天天刷屏。但有一个东西,很多人天天在用,却说不清楚它到底是个啥——向量数据库。
我自己第一次接触向量数据库的时候也是一脸懵,心想这不就是个数据库吗,跟MySQL有啥区别?后来真正在项目里用上了,才发现这玩意儿跟传统数据库完全是两个物种。今天我就把自己踩过的坑、理解的过程、还有生产环境里的一些实践经验,一股脑全倒出来,希望能帮你们少走点弯路。
先搞明白一个前提:什么是向量
聊向量数据库之前,得先搞清楚"向量"这个词。不然你连它存的是啥都不知道,更别提怎么用了。

向量这东西说白了就是一组数字。比如 [0.12, -0.34, 0.56, 0.78, ...],就这么简单。但别小看这组数字,它可以用来表示任何东西——一段文字、一张图片、一段音频、甚至一个用户的行为特征。
举个最接地气的例子。你把"苹果"这个词丢给一个embedding模型(比如OpenAI的text-embedding-ada-002),它会吐出来一个1536维的向量,大概长这样:[0.0023, -0.0145, 0.0876, ...],一共1536个浮点数。这1536个数字就是"苹果"这个概念在数学空间里的"坐标"。
你可能会问,这有啥用?妙就妙在这里——语义相近的词,它们在向量空间里的距离也近。"苹果"和"水果"的向量距离,肯定比"苹果"和"汽车"的向量距离要近得多。这就叫语义相似性。
再想象一下,如果你把公司里所有的文档、知识库、FAQ全都转成向量,然后当用户提问的时候,把问题也转成向量,在所有文档向量里找最近的那些——这不就是语义搜索吗?这比关键词搜索强太多了,用户问"怎么重置密码",你能匹配到"忘记密码怎么办",关键词搜索可做不到这个。
那向量数据库到底是干嘛的
理解了向量是什么,向量数据库就好理解了。它就是专门用来存这些向量,并且能快速做"相似性搜索"的数据库。
传统数据库比如MySQL,你查数据基本就是精确匹配或者范围查询。SELECT * FROM users WHERE age > 18 AND city = '北京',这种查询是确定性的,条件写死了,结果也是确定的。
但向量数据库不一样,它的核心操作是:给我一个向量,找出跟它最相似的K个向量。这叫ANN查询,全称是Approximate Nearest Neighbor,近似最近邻搜索。注意那个"Approximate",是近似的,不是精确的。为了速度,向量数据库牺牲了一点点精度,换来了数量级的性能提升。
我之前在项目里做过一个测试,200万条768维的向量,用暴力搜索(也就是逐个比较),一次查询要好几秒。换成Milvus的IVF_FLAT索引,查询时间降到毫秒级。这个差距,在生产环境里就是可用和不可用的区别。
向量数据库的核心概念
这个部分可能有点枯燥,但我尽量用大白话讲。你要是用向量数据库,这几个概念绕不过去。
Collection / 表
跟MySQL里的表差不多,一个Collection就是存一类向量的容器。比如你有一个"产品描述"的Collection,一个"用户画像"的Collection。
向量字段 + 标量字段
这是向量数据库跟传统数据库一个很大的区别。一条记录里,既有向量字段(用来做相似性搜索),也有标量字段(就是普通的字符串、数字、布尔值,用来做过滤)。比如一条记录可能是这样的:
{
"id": "doc_001",
"content": "我们的产品支持7天无理由退货",
"vector": [0.12, -0.34, 0.56, ...], // 768维
"category": "售后政策",
"department": "客服部",
"created_at": "2024-01-15"
}vector字段用来做语义搜索,category、department这些标量字段用来做过滤。这就是所谓的"混合搜索",后面会细讲。
索引类型
这个是向量数据库最核心也最容易踩坑的地方。不同的索引类型,在查询速度、召回率、内存占用之间做不同的权衡。

常见的几种:
FLAT:暴力搜索,逐个比较。最准,但最慢。数据量小的时候(几万条以内)可以用,数据量大了就别想了。
IVF_FLAT:先把所有向量聚成N个簇(用K-Means),查询的时候只在最近的几个簇里搜索。速度比FLAT快很多,召回率会有点损失,但可以通过调节nprobe参数来平衡。我生产环境里200万条数据用的就是这个,nprobe设的16,召回率大概95%左右,够用了。
IVF_PQ:在IVF的基础上加了Product Quantization(乘积量化),把向量压缩存储。内存占用大幅降低,但召回率也会进一步下降。数据量特别大、内存又不够的时候可以考虑。
HNSW:基于图结构的索引,目前综合性能最好的之一。查询速度极快,召回率也很高,但构建索引慢,内存占用大。如果你对查询延迟要求特别高,HNSW是首选。Pinecone底层就是用的类似HNSW的算法。
DiskANN:微软开源的方案,把索引放在磁盘上,内存只放一部分。适合超大规模数据集(上亿级别),但查询延迟会比纯内存方案高一些。
怎么选?我给个简单的建议:数据量10万以内用FLAT就行;10万到500万用IVF_FLAT或HNSW;500万以上考虑IVF_PQ或DiskANN。当然这只是粗略的参考,具体还得看你的硬件资源和性能要求。
距离度量

计算两个向量之间的相似度,常用的有三种:
L2距离(欧氏距离):两个向量在空间中的直线距离。值越小越相似。
内积(IP, Inner Product):两个向量的点积。值越大越相似。如果你用的embedding模型输出的向量是归一化的(模长为1),那内积就等于余弦相似度。
余弦相似度:两个向量夹角的余弦值。范围[-1, 1],值越大越相似。这是NLP领域最常用的度量方式。
这里有个坑要注意:你在选距离度量的时候,一定要跟你embedding模型训练时用的度量方式一致。比如OpenAI的embedding模型用的是余弦相似度,你在向量数据库里就得用余弦或者内积(归一化向量情况下)。用L2的话效果会打折扣,别问我怎么知道的。
主流向量数据库横评
现在市面上的向量数据库一大堆,我挑几个有代表性的说说。
Milvus

开源界的扛把子,最早是浙大和Zilliz搞的,现在已经是LF AI基金会毕业项目了。支持分布式部署,生态完善,各种索引类型都支持。我们公司生产环境用的就是Milvus,跑了快两年了,整体还算稳定。
优点:功能全面、社区活跃、支持分布式、云原生架构。
缺点:架构比较重,组件多(etcd、MinIO、Pulsar),部署运维有一定复杂度。小团队用起来有点杀鸡用牛刀的感觉。
如果你数据量在百万级以上,需要分布式,Milvus是个很稳的选择。
Qdrant

Rust写的,性能很好,轻量级。单节点部署特别简单,docker一行命令就起来了。也支持分布式集群。
优点:轻量、性能好、API设计优雅、过滤功能强大。
缺点:生态不如Milvus完善,大规模集群的生产案例相对少一些。
中小规模项目我挺推荐Qdrant的,上手快,性能也不差。
Weaviate
Go写的,内置了跟各种embedding模型的集成(OpenAI、Cohere、HuggingFace等),你可以直接把原始文本丢进去,它自动帮你做embedding。
优点:集成度高、支持多模态、GraphQL查询接口。
缺点:性能在大规模场景下不如Milvus和Qdrant,灵活性稍差。
pgvector
PostgreSQL的向量搜索扩展。如果你已经有PG了,只是想加个向量搜索功能,pgvector是最省事的选择。不用引入新组件,直接在PG里建表、建索引就行。但性能跟专业向量数据库比有差距,数据量大了(百万级以上)会比较吃力。
我个人建议:新项目、数据量大的,选Milvus或Qdrant;已有PG、数据量不大的,pgvector够用;不想运维的,Pinecone或者Zilliz Cloud(Milvus的商业版)。
混合搜索:向量数据库的杀手锏
纯向量搜索其实有局限性的。我举个真实的例子。
我们做智能客服的时候,用户问"退货流程是什么",纯向量搜索能找到语义相近的文档,这没问题。但用户如果问"2024年1月之后的退货政策",纯向量搜索就傻了——"2024年1月之后"这是个时间范围过滤,语义搜索搞不定这种精确条件。
这时候就需要混合搜索了:先通过标量字段(created_at)过滤出2024年1月之后的记录,再在过滤后的结果里做向量相似性搜索。
Milvus和Qdrant都原生支持这种"先过滤再搜索"的模式。语法大概长这样(Milvus的Python SDK):
results = collection.search(
data=[query_vector],
anns_field="vector",
param={"metric_type": "COSINE", "params": {"nprobe": 16}},
limit=10,
expr='created_at > "2024-01-01" and category == "售后政策"' # 标量过滤
)这个expr参数就是标量过滤条件,跟SQL的WHERE子句差不多。向量数据库先执行这个过滤,缩小候选集,再做ANN搜索。这样既保证了语义相关性,又满足了精确过滤的需求。
还有一种混合搜索是向量搜索+关键词搜索(BM25),把两路结果做融合排序。这个在RAG场景里特别有用,因为有些关键词(比如产品型号、专有名词)语义搜索反而匹配不好,BM25能补上这个短板。Weaviate和Qdrant都支持这种多路召回融合。
一个完整的RAG实战案例
说了这么多,来个实际的例子。假设我们要给公司内部知识库做一个智能问答系统,大概流程是这样的:
第一步:数据准备
把公司知识库的文档(PDF、Word、Markdown等)全部解析出来,按段落切分。切分的时候注意,不能太长也不能太短。太长了embedding会丢失细节信息,太短了上下文不够。一般建议200-500个token一段,段落之间可以有少量重叠。
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
separators=["\n\n", "\n", "。", "!", "?", ".", " "]
)
chunks = splitter.split_text(document_text)第二步:生成向量
把每个chunk丢给embedding模型,拿到向量。
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('BAAI/bge-large-zh-v1.5')
vectors = model.encode(chunks, normalize_embeddings=True)这里normalize_embeddings=True很关键,归一化之后内积就等于余弦相似度,后面搜索的时候用内积就行,计算更快。
第三步:写入向量数据库
from pymilvus import Collection, FieldSchema, CollectionSchema, DataType
# 定义schema
fields = [
FieldSchema(name="id", dtype=DataType.VARCHAR, max_length=64, is_primary=True),
FieldSchema(name="vector", dtype=DataType.FLOAT_VECTOR, dim=1024),
FieldSchema(name="content", dtype=DataType.VARCHAR, max_length=2048),
FieldSchema(name="source", dtype=DataType.VARCHAR, max_length=256),
FieldSchema(name="department", dtype=DataType.VARCHAR, max_length=64),
]
schema = CollectionSchema(fields=fields)
collection = Collection(name="knowledge_base", schema=schema)
# 创建索引
index_params = {
"metric_type": "IP", # 内积,因为向量已经归一化了
"index_type": "HNSW",
"params": {"M": 16, "efConstruction": 256}
}
collection.create_index(field_name="vector", index_params=index_params)
# 插入数据
data = [
ids, # id列表
vectors, # 向量列表
contents, # 原文列表
sources, # 来源列表
departments # 部门列表
]
collection.insert(data)
collection.load() # 加载到内存第四步:查询
query = "公司年假怎么请"
query_vector = model.encode([query], normalize_embeddings=True)[0]
results = collection.search(
data=[query_vector.tolist()],
anns_field="vector",
param={"metric_type": "IP", "params": {"ef": 128}},
limit=5,
output_fields=["content", "source", "department"]
)
for hit in results[0]:
print(f"相似度: {hit.distance:.4f}, 内容: {hit.entity.get('content')[:100]}")
print(f"来源: {hit.entity.get('source')}")第五步:把搜索结果喂给大模型
把top-K的搜索结果拼在一起,作为上下文,跟用户的问题一起发给大模型,让它生成回答。这就是RAG(Retrieval-Augmented Generation)的核心流程。
context = "\n\n".join([hit.entity.get('content') for hit in results[0]])
prompt = f"""基于以下参考资料回答用户的问题。如果参考资料中没有相关信息,请说"我不确定"。
参考资料:
{context}
用户问题:{query}
回答:"""
# 然后把prompt发给GPT或其他大模型整个流程跑通不难,但要做出好效果,每个环节都有优化空间。切分策略怎么调、embedding模型怎么选、检索参数怎么设、prompt怎么写、要不要加rerank……这些细节决定了最终体验的好坏。
向量数据库的未来趋势
最后聊几点我观察到的趋势。
多模态是方向。现在大部分向量数据库还是以文本为主,但图片、视频、音频的向量搜索需求在快速增长。比如"以图搜图"、"哼唱找歌"这些场景。Milvus和Qdrant都在加强多模态支持。
Serverless化。Pinecone已经推出了Serverless方案,按查询量计费,不用预置资源。Zilliz Cloud也有Serverless选项。对中小团队来说,这种模式能大幅降低成本和运维负担。
跟AI框架的深度集成。LangChain、LlamaIndex这些框架已经内置了对主流向量数据库的支持,未来集成只会越来越深。向量数据库正在成为AI应用基础设施的一部分,就像MySQL之于Web应用。
向量数据库+传统数据库的融合。pgvector这种方案是一种思路,把向量搜索能力加到传统数据库里。另一种思路是向量数据库加强标量查询能力。两个方向都在演进,未来可能会出现更统一的方案。
写在最后
向量数据库这东西,说复杂也复杂,说简单也简单。核心就是存向量、搜向量,但要做好,每个环节都有讲究。embedding模型的选择、索引类型的配置、距离度量的匹配、混合搜索的设计、生产环境的运维,这些都需要你在实践中慢慢摸索。
我自己的体会是,别被各种概念和术语吓到。先从一个小的场景入手,比如给团队的知识库做个语义搜索,把整个链路跑通。然后逐步优化,加过滤、加rerank、调参数。跑过一遍之后,很多之前不理解的东西自然就明白了。
向量数据库不是万能的,它只是AI应用技术栈中的一环。但这一环在RAG场景里确实不可替代。大模型有知识截止日期、有幻觉问题,向量数据库提供的精准检索能力,是缓解这些问题的重要手段。
如果你正在做AI相关的项目,还没接触过向量数据库,建议赶紧动手试试。光看文章不动手,永远都是纸上谈兵。
觉得这篇文章对你有帮助的话,点个赞、转发一下呗,让更多人看到。有什么问题也欢迎留言讨论,我尽量回复。
公众号:耕云躬行录
个人博客:躬行笔记