RAG 系列(六):向量数据库——存储与检索的基础设施并不只看表面做法,关键还要理解相关条件、限制和后续影响。
前面五篇文章,我们搞清楚了怎么切分文档、怎么生成 Embedding。现在这些向量存在哪里?怎么被高效地检索出来?

你可能会想:"用 Redis 或者 PostgreSQL 存向量不行吗?"
不行——传统数据库是为精确查询设计的(比如 WHERE id = 123),而向量检索是近似最近邻搜索(ANN):给定一个查询向量,在几亿个文档向量中快速找出最相似的 Top-K 个。传统数据库的索引(B+树、哈希表)对这种"相似性查询"无能为力。
举个例子:
查找 id=42 的用户 → O(1) 或 O(log n)查找和用户 A 兴趣最相似的 10 个人 → 需要比较全库向量,O(n) 暴力搜索太慢了向量数据库通过专门的 ANN 索引(HNSW、IVF 等),把 O(n) 降到 O(log n),在毫秒级完成亿级向量的相似性检索。
存储海量向量(百万到十亿级别),每个向量附带原始文本和元数据。支持增量写入和删除。
核心算法:
| 算法 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| HNSW | 分层导航小世界图,构建多层图结构,从粗到细搜索 | 速度快、精度高、支持动态增删 | 内存占用较大 |
| IVF | Inverted File,把向量空间分成多个聚类,先找最近聚类再搜索 | 内存友好、适合静态数据 | 增删向量需要重建索引 |
| Flat | 暴力全量比较 | 100% 精确 | 极慢,仅适合小数据量 |
选型建议:开发阶段用 Flat(简单),生产环境用 HNSW(性能最好)。
这是向量数据库区别于纯向量检索库(如 FAISS)的关键能力。你可以同时做两件事:
时间 > 2024-01-01 AND 类别 = "技术文档")# 示例:只检索 2024 年之后的技术文档results = vectorstore.similarity_search( query="微服务监控", k=5, filter={ "category": "技术文档", "year": {"$gte": 2024} })| 数据库 | 定位 | 部署方式 | 索引算法 | 元数据过滤 | 适用场景 |
|---|---|---|---|---|---|
| Chroma | 开发/原型 | 本地/嵌入式 | HNSW | ✅ | 本地快速验证、小项目 |
| Qdrant | 生产自部署 | Docker/K8s | HNSW | ✅ | 企业首选,性能强、过滤强 |
| Weaviate | 混合检索 | Docker/托管 | HNSW | ✅ | 需要 BM25 + 向量混合检索 |
| pgvector | PG 扩展 | PostgreSQL 插件 | HNSW/IVF | ✅ | 已有 PG 环境,不想引入新数据库 |
| Pinecone | 托管云服务 | 全托管 | 自动选择 | ✅ | 不想运维,快速上线 |
Chroma —— 开发者的最佳选择
from langchain_chroma import Chroma# 嵌入式运行,零配置vectorstore = Chroma.from_documents( documents=chunks, embedding=embeddings, persist_directory="./chroma_db")Qdrant —— 生产环境的企业首选
from langchain_qdrant import Qdrantfrom qdrant_client import QdrantClientclient = QdrantClient(url="http://localhost:6333")vectorstore = Qdrant( client=client, collection_name="docs", embeddings=embeddings,)Weaviate —— 混合检索专家
from langchain_weaviate import WeaviateVectorStoreimport weaviateclient = weaviate.connect_to_local()vectorstore = WeaviateVectorStore( client=client, index_name="Docs", text_key="text", embedding=embeddings,)pgvector —— PostgreSQL 用户的福音
-- 在 PostgreSQL 中安装扩展CREATE EXTENSION vector;-- 创建带向量列的表CREATETABLE documents ( id SERIAL PRIMARY KEY, content TEXT, embedding vector(1024), category VARCHAR(50));-- 创建 HNSW 索引CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops);from langchain_community.vectorstores import PGVectorvectorstore = PGVector( connection_string="postgresql://user:pass@localhost/db", embedding_function=embeddings, collection_name="docs",)Pinecone —— 完全不想运维的选择
from langchain_pinecone import PineconeVectorStorefrom pinecone import Pineconepc = Pinecone(api_key="your-key")index = pc.Index("docs")vectorstore = PineconeVectorStore(index=index, embedding=embeddings)假设我们要为一个技术博客系统构建 RAG:
from langchain_chroma import Chromafrom langchain_openai import OpenAIEmbeddingsembeddings = OpenAIEmbeddings( model="BAAI/bge-large-zh-v1.5", api_key=os.getenv("SILICONFLOW_API_KEY"), base_url="https://api.siliconflow.cn/v1", chunk_size=32,)# 创建本地向量库vectorstore = Chroma.from_documents( documents=chunks, embedding=embeddings, persist_directory="./chroma_db", collection_metadata={"hnsw:space": "cosine"})# 检索results = vectorstore.similarity_search("微服务拆分原则", k=3)for doc in results: print(f"来源: {doc.metadata['source']}") print(f"内容: {doc.page_content[:100]}...") print()# 持久化(Chroma 自动保存)# 下次加载:# vectorstore = Chroma(persist_directory="./chroma_db", embedding_function=embeddings)from langchain_qdrant import Qdrantfrom qdrant_client import QdrantClient, models# 连接 Qdrant 服务client = QdrantClient(url="http://localhost:6333")# 创建 Collection(相当于数据库的表)client.create_collection( collection_name="blog_docs", vectors_config=models.VectorParams( size=1024, # BGE-large-zh 的维度 distance=models.Distance.COSINE, ),)# 写入数据vectorstore = Qdrant( client=client, collection_name="blog_docs", embeddings=embeddings,)vectorstore.add_documents(documents=chunks)# 带元数据过滤的检索results = vectorstore.similarity_search( query="微服务监控", k=5, filter=models.Filter( must=[ models.FieldCondition( key="category", match=models.MatchValue(value="微服务") ), models.FieldCondition( key="year", range=models.Range(gte=2024) ), ] ))# 1. 从 Chroma 导出croma_store = Chroma( persist_directory="./chroma_db", embedding_function=embeddings)all_docs = chroma_store.get()# 2. 导入 Qdrantqdrant_store = Qdrant( client=qdrant_client, collection_name="blog_docs", embeddings=embeddings,)# 3. 批量写入(Qdrant 支持高效批量导入)from langchain_core.documents import Documentdocs = [ Document(page_content=text, metadata=meta) for text, meta inzip(all_docs["documents"], all_docs["metadatas"])]qdrant_store.add_documents(docs)向量数据库在比较两个向量时,需要一种"距离度量"。常用的有三种:
cosine(A, B) = (A · B) / (||A|| × ||B||)dot(A, B) = A · B = Σ(Ai × Bi)euclidean(A, B) = √Σ(Ai - Bi)²| 场景 | 推荐算法 | 理由 |
|---|---|---|
| 文本语义检索 | 余弦相似度 | 主流选择,对向量长度不敏感 |
| 推荐系统 | 点积 | 考虑用户兴趣强度 |
| 图像检索 | 欧氏距离 | 像素级差异更直观 |
| 不确定 | 余弦相似度 | 最安全的选择 |
重要:Embedding 模型和相似度算法要匹配!BGE 模型推荐使用余弦相似度,OpenAI text-embedding-3 系列也推荐余弦相似度。
假设你的知识库有 10 万篇文档,涵盖技术、产品、运营、销售多个部门。用户问:"今年的销售目标是什么?"
纯向量检索可能召回:
加上元数据过滤 {"department": "销售", "year": 2024},就能精准锁定范围。
from langchain_chroma import Chroma# 写入时带上元数据docs = [ Document( page_content="2024 年销售目标:营收增长 30%...", metadata={"department": "销售", "year": 2024, "type": "目标"} ), Document( page_content="销售系统使用 Redis 缓存...", metadata={"department": "技术", "year": 2024, "type": "架构"} ),]vectorstore = Chroma.from_documents(docs, embeddings)# 检索时过滤results = vectorstore.similarity_search( "销售目标", k=3, filter={"department": "销售", "year": 2024})from qdrant_client import modelsfilter = models.Filter( must=[ # AND 条件 models.FieldCondition(key="department", match=models.MatchValue(value="销售")), models.FieldCondition(key="year", range=models.Range(gte=2024)), ], should=[ # OR 条件 models.FieldCondition(key="type", match=models.MatchValue(value="目标")), models.FieldCondition(key="type", match=models.MatchValue(value="总结")), ], must_not=[ # NOT 条件 models.FieldCondition(key="status", match=models.MatchValue(value="草稿")), ])| 场景 | 推荐数据库 | 理由 |
|---|---|---|
| 本地开发/快速原型 | Chroma | 零配置,pip install 即用 |
| 生产环境自部署 | Qdrant | 性能最强,过滤最灵活,Rust 稳定 |
| 需要混合检索(BM25 + 向量) | Weaviate | 原生支持两种检索融合 |
| 已有 PostgreSQL | pgvector | 不引入新组件,SQL 表达能力完整 |
| 完全不想运维 | Pinecone | 全托管,自动扩缩 |
| 超大规模(十亿级向量) | Milvus | 专为海量向量设计(本文未展开) |
阶段 1:开发验证 └── Chroma(本地嵌入式,零配置) ↓阶段 2:测试环境 └── Qdrant Docker(单节点,验证功能) ↓阶段 3:生产上线 └── Qdrant 集群 / Pinecone 托管(高可用)本文覆盖了向量数据库的核心知识:
关键认知:向量数据库不是越贵越好,而是越适合你的场景越好。开发用 Chroma,生产用 Qdrant,有 PG 用 pgvector,不想运维用 Pinecone——没有银弹,只有最合适。