LanceDB · ChromaDB · Pinecone · Weaviate · Qdrant · Milvus · pgvector · Vald
从嵌入式到云原生,选对向量数据库是 RAG 和 Agent 系统的地基
传统数据库用精确匹配和关键词搜索——你搜 "apple",它只返回包含 "apple" 的行。但语言不是这样工作的:
numpy 暴力搜索就够了Embedding 模型将非结构化数据转化为固定维度的浮点向量。常见模型:
| 模型 | 维度 | MTEB 排名 | 特点 |
|---|---|---|---|
| OpenAI text-embedding-3-large | 3072 | ⭐⭐⭐⭐ | 质量高、成本高、可降维 |
| OpenAI text-embedding-3-small | 1536 | ⭐⭐⭐ | 性价比最佳 |
| Cohere embed-v3 | 1024 | ⭐⭐⭐⭐ | 多语言优、支持搜索类型参数 |
| BGE-M3 (BAAI) | 1024 | ⭐⭐⭐⭐ | 开源最强、多语言、多粒度 |
| Nomic Embed | 768 | ⭐⭐⭐ | 开源、8192 上下文、小模型 |
| all-MiniLM-L6-v2 | 384 | ⭐⭐ | 极速、离线可用、质量一般 |
最常用。衡量方向相似性,忽略向量长度。范围 [-1, 1]。
cos(A, B) = (A · B) / (||A|| × ||B||)
✅ 归一化后等价于内积,计算快
✅ 对向量尺度不敏感,最通用
直线距离。对向量长度敏感。
L2(A, B) = sqrt(Σ(Ai - Bi)²)
⚠️ 归一化向量上等价于余弦
❌ 非归一化向量上,长度差异会干扰
实践建议:大多数场景用余弦相似度。如果 Embedding 模型已做归一化(OpenAI 的就是),内积 = 余弦,直接用 dot product 更快。
精确 KNN 搜索是 O(n),百万级以上不可接受。ANN (Approximate Nearest Neighbor) 用索引换取速度:
| 索引类型 | 原理 | 查询延迟 | 召回率 | 构建速度 | 适用场景 |
|---|---|---|---|---|---|
| HNSW | 多层跳表图 | ~1ms | 99%+ | 中等 | 通用首选 |
| IVF | 倒排聚类 | ~5ms | 95%+ | 快 | 超大规模 |
| IVF-PQ | 聚类+量化 | ~10ms | 90%+ | 快 | 内存受限 |
| Flat | 暴力搜索 | 慢 | 100% | 即开即用 | <10万条 |
| DiskANN | 磁盘驻留图 | ~5ms | 95%+ | 慢 | 超大规模+内存不够 |
LanceDB 是一个基于 Lance 列式格式的嵌入式向量数据库,用 Rust 编写。它不运行服务器——你的应用直接读写磁盘上的文件。类似于 SQLite 之于传统数据库,LanceDB 是向量数据库的 "SQLite"。
# pip install lancedb
import lancedb
import numpy as np
# 连接(本地目录,自动创建)
db = lancedb.connect("./my_lance_db")
# 创建表
data = [
{"text": "向量数据库是 AI 应用的基础设施", "vector": np.random.rand(768).tolist(), "source": "doc1"},
{"text": "LanceDB 用 Rust 编写,嵌入式架构", "vector": np.random.rand(768).tolist(), "source": "doc2"},
{"text": "HNSW 索引在百万级数据上仅需 1ms", "vector": np.random.rand(768).tolist(), "source": "doc3"},
]
table = db.create_table("documents", data)
# 向量搜索
query_vector = np.random.rand(768).tolist()
results = table.search(query_vector).limit(5).to_pandas()
print(results[["text", "source", "_distance"]])
# 创建 IVF-PQ 索引加速(数据量大时)
table.create_index(
num_partitions=256, # IVF 聚类数
num_sub_vectors=96, # PQ 量化子向量数
)
# 过滤搜索:元数据过滤 + 向量搜索
results = (
table.search(query_vector)
.where("source = 'doc1'", prefilter=True)
.limit(5)
.to_pandas()
)
# 时间旅行:查看上一版本的数据
old_version = db.open_table("documents", version=0)
print(f"当前版本: {table.version}, 旧版本: 0")
# S3 远程存储
db_remote = lancedb.connect("s3://my-bucket/lancedb")
ChromaDB(简称 Chroma)是为 AI 应用设计的开源向量数据库,最大特点是 内置 Embedding 函数——你不需要自己调 OpenAI API 做向量化,Chroma 自动帮你做。它是 LangChain 生态最紧密集成的向量数据库。
all-MiniLM-L6-v2,一行代码配置 OpenAI/CoherePersistentClient)和 Client-Server(HttpClient)$eq, $ne, $gt, $gte, $in, $nin 等操作符pip install chromadb → 3 行代码开始用# pip install chromadb
import chromadb
# 嵌入式模式(数据持久化到磁盘)
client = chromadb.PersistentClient(path="./chroma_db")
# 创建 Collection(自动使用默认 Embedding 函数)
collection = client.get_or_create_collection(
name="knowledge_base",
metadata={"hnsw:space": "cosine"} # 距离度量
)
# 添加文档——Chroma 自动做 Embedding!
collection.add(
documents=[
"向量数据库是 AI 时代的存储基础设施",
"ChromaDB 让你 3 行代码开始用向量搜索",
"RAG 系统的核心是检索质量",
],
metadatas=[
{"source": "intro", "page": 1},
{"source": "quickstart", "page": 1},
{"source": "rag-guide", "page": 3},
],
ids=["doc1", "doc2", "doc3"]
)
# 语义搜索——直接传文本,Chroma 自动 Embedding
results = collection.query(
query_texts=["什么是向量数据库?"],
n_results=3,
where={"source": "intro"}, # 元数据过滤
)
print(results["documents"])
# 也可以手动传向量(用自己的 Embedding 模型)
import openai
resp = openai.embeddings.create(
model="text-embedding-3-small",
input="什么是向量数据库?"
)
query_vector = resp.data[0].embedding
results = collection.query(
query_embeddings=[query_vector],
n_results=3,
)
Pinecone 是向量数据库领域的先行者和商业化最成功的案例。你不需要管理任何基础设施——注册账号、创建 Index、开始写入和查询。2024 年推出的 Serverless 模式更是零固定成本起步。
# pip install pinecone
from pinecone import Pinecone
pc = Pinecone(api_key="your-api-key")
# 创建 Serverless Index
pc.create_index(
name="knowledge",
dimension=1536,
metric="cosine",
spec={
"serverless": {
"cloud": "aws",
"region": "us-east-1"
}
}
)
index = pc.Index("knowledge")
# 批量 Upsert
index.upsert(
vectors=[
{"id": "doc1", "values": [0.1, 0.2, ...], "metadata": {"source": "wiki", "lang": "zh"}},
{"id": "doc2", "values": [0.3, 0.4, ...], "metadata": {"source": "blog", "lang": "en"}},
],
namespace="production" # 多租户隔离
)
# 查询 + 元数据过滤
results = index.query(
vector=[0.15, 0.25, ...],
top_k=10,
filter={"lang": {"$eq": "zh"}, "source": {"$in": ["wiki", "blog"]}},
namespace="production",
include_metadata=True,
)
# 混合搜索(Sparse-Dense Index)
results = index.query(
vector={"values": [0.15, 0.25, ...], "sparse_values": {"indices": [2, 5, 10], "values": [0.3, 0.5, 0.2]}},
top_k=10,
include_metadata=True,
)
| 模式 | 写入 | 查询 | 存储 | 免费层 |
|---|---|---|---|---|
| Serverless | $2.50/1M vectors | $0.10/1K queries | $0.096/GB/月 | 1 Index, 5GB |
| Pod (s1) | 包含在 Pod 费 | 包含在 Pod 费 | 包含在 Pod 费 | 无 |
Weaviate 是一个将向量搜索与知识图谱融合的数据库。它最独特的卖点是 Modules 系统——内置多种 Embedding 模型的集成(OpenAI、Cohere、HuggingFace、多模态 CLIP 等),以及 Generative Search(直接在数据库层调用 LLM 做 RAG)。
with_generate() 一行做 RAG,无需自己编排# pip install weaviate-client
import weaviate
import weaviate.classes as wvc
# 连接本地 Weaviate
client = weaviate.connect_to_local()
# 创建 Collection
collection = client.collections.create(
name="Article",
properties=[
wvc.config.Property(name="title", data_type=wvc.config.DataType.TEXT),
wvc.config.Property(name="content", data_type=wvc.config.DataType.TEXT),
wvc.config.Property(name="category", data_type=wvc.config.DataType.TEXT),
],
vectorizer_config=wvc.config.Configure.Vectorizer.text2vec_openai(
model="text-embedding-3-small"
),
# 多租户
multi_tenancy_config=wvc.config.Configure.multi_tenancy(enabled=True),
)
# 插入数据(自动向量化)
collection.with_tenant("tenant_A").data.insert({
"title": "向量数据库入门",
"content": "向量数据库是 AI 应用的核心基础设施...",
"category": "tutorial",
})
# 语义搜索
results = collection.with_tenant("tenant_A").query.near_text(
query="如何选择向量数据库?",
limit=5,
filters=wvc.query.Filter.by_property("category").equal("tutorial"),
)
# Generative Search(数据库层 RAG!)
from weaviate.classes.query import MetadataQuery
results = collection.with_tenant("tenant_A").generate.near_text(
query="总结这些文章的关键要点",
limit=5,
grouped_task="请用中文总结以上文章的三个核心观点",
)
print(results.generated)
Qdrant 是用 Rust 编写的高性能向量搜索引擎。它在内存效率和查询延迟方面表现优异,特别适合需要 复杂过滤 + 向量搜索 的场景——这是很多 SaaS 应用(电商、内容平台)的核心需求。
# pip install qdrant-client
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct, Filter, FieldCondition, MatchValue
client = QdrantClient(host="localhost", port=6333)
# 创建 Collection
client.create_collection(
collection_name="products",
vectors_config=VectorParams(size=1536, distance=Distance.COSINE),
)
# 插入数据
client.upsert(
collection_name="products",
points=[
PointStruct(
id=1,
vector=[0.1, 0.2, ...],
payload={"name": "MacBook Pro", "category": "laptop", "price": 14999}
),
PointStruct(
id=2,
vector=[0.3, 0.4, ...],
payload={"name": "iPhone 18", "category": "phone", "price": 8999}
),
]
)
# 向量搜索 + 复杂过滤(Qdrant 的杀手锏!)
results = client.search(
collection_name="products",
query_vector=[0.15, 0.25, ...],
query_filter=Filter(
must=[
FieldCondition(key="category", match=MatchValue(any=["laptop", "phone"])),
FieldCondition(key="price", range={"lte": 15000}),
]
),
limit=10,
)
# 启用量化节省内存
client.update_collection(
collection_name="products",
quantization_config=models.ScalarQuantization(
scalar=models.ScalarQuantizationConfig(
type=models.ScalarType.INT8,
quantile=0.99,
always_ram=True,
),
),
)
Milvus 是面向超大规模场景的分布式向量数据库,背靠 Zilliz 公司。当数据量超过 10 亿条、需要多机并行搜索时,Milvus 是最成熟的选择。Zilliz Cloud 提供全托管版本。
# pip install pymilvus
from pymilvus import MilvusClient, DataType
client = MilvusClient(uri="http://localhost:19530")
# 创建 Collection
client.create_collection(
collection_name="articles",
dimension=1536,
metric_type="COSINE",
auto_id=True,
)
# 也可以用 Schema 定义(支持标量字段 + 向量字段)
from pymilvus import CollectionSchema, FieldSchema, Collection, AnnIndex
schema = CollectionSchema(fields=[
FieldSchema("id", DataType.INT64, is_primary=True, auto_id=True),
FieldSchema("title", DataType.VARCHAR, max_length=512),
FieldSchema("category", DataType.VARCHAR, max_length=64),
FieldSchema("embedding", DataType.FLOAT_VECTOR, dim=1536),
])
collection = Collection("articles_v2", schema)
# 创建索引
index_params = client.prepare_index_params()
index_params.add_index(
field_name="embedding",
index_type="HNSW",
metric_type="COSINE",
params={"M": 16, "efConstruction": 256}
)
client.create_index("articles_v2", index_params)
# 插入 + 搜索
data = [
{"title": "向量数据库选型指南", "category": "tutorial", "embedding": [0.1, 0.2, ...]},
]
client.insert("articles_v2", data)
results = client.search(
collection_name="articles_v2",
data=[[0.15, 0.25, ...]],
limit=10,
filter='category == "tutorial"',
output_fields=["title", "category"],
)
pgvector 是 PostgreSQL 的向量搜索扩展。如果你已经在用 PostgreSQL,它是加入向量搜索 成本最低 的方式——不需要新服务、新运维、新 SDK。
SELECT * FROM docs ORDER BY embedding <=> '[0.1,0.2,...]' LIMIT 10-- 安装扩展
CREATE EXTENSION IF NOT EXISTS vector;
-- 创建表
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
content TEXT,
source VARCHAR(255),
embedding vector(1536)
);
-- 插入数据
INSERT INTO documents (content, source, embedding)
VALUES ('向量数据库入门', 'tutorial', '[0.1, 0.2, ...]'::vector);
-- 创建 HNSW 索引
CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
-- 向量搜索 + 过滤 + JOIN!
SELECT d.content, d.source, 1 - (d.embedding <=> '[0.15, 0.25, ...]'::vector) AS similarity
FROM documents d
JOIN user_permissions up ON d.source = up.allowed_source
WHERE up.user_id = 42
AND d.source != 'internal'
ORDER BY d.embedding <=> '[0.15, 0.25, ...]'::vector
LIMIT 10;
-- 设置搜索参数
SET hnsw.ef_search = 100; -- 越高召回率越好,越慢
# Python (SQLAlchemy)
from pgvector.sqlalchemy import Vector
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import declarative_base
Base = declarative_base()
class Document(Base):
__tablename__ = "documents"
id = Column(Integer, primary_key=True)
content = Column(String)
source = Column(String)
embedding = Column(Vector(1536))
# 查询
from sqlalchemy import select
stmt = (
select(Document, Document.embedding.cosine_distance(query_vec).label("distance"))
.order_by(Document.embedding.cosine_distance(query_vec))
.limit(10)
)
results = session.execute(stmt).all()
| 维度 | LanceDB | ChromaDB | Pinecone | Weaviate | Qdrant | Milvus | pgvector |
|---|---|---|---|---|---|---|---|
| 开源 | ✅ Apache 2.0 | ✅ Apache 2.0 | ❌ 闭源 | ✅ BSD-3 | ✅ Apache 2.0 | ✅ Apache 2.0 | ✅ PostgreSQL |
| 语言 | Rust | Python | — | Go | Rust | Go+C++ | C |
| 部署模式 | 嵌入式 | 嵌入/CS | 全托管 | CS/托管 | CS/托管 | CS/托管 | PG 扩展 |
| 数据规模 | ~千万 | ~百万 | ~亿 | ~亿 | ~亿 | 百亿+ | ~五百万 |
| 查询延迟 | <1ms | 1-5ms | 20-50ms | 5-20ms | <1ms | 10-50ms | 5-20ms |
| 内置 Embedding | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
| 混合搜索 | ❌ | ❌ | ✅ Sparse-Dense | ✅ BM25+向量 | ❌ (仅过滤) | ✅ 全文+向量 | ✅ SQL+向量 |
| 多租户 | ❌ | ❌ | ✅ Namespace | ✅ 物理隔离 | ✅ Payload | ✅ Partition | ✅ RLS |
| 量化支持 | IVF-PQ | ❌ | ❌ | ❌ | SQ/PQ/BQ | SQ/PQ/GPU | ❌ |
| 运维复杂度 | 零 | 低 | 零 | 中 | 中 | 高 | 低 |
| 云托管 | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ Zilliz | ✅ Supabase |
| 场景 | 推荐 | 理由 |
|---|---|---|
| 个人项目 / MVP | LanceDB | 零部署,嵌入式,3 行代码 |
| Jupyter 实验 | ChromaDB | 内置 Embedding,Python 原生 |
| SaaS 产品(已有 PG) | pgvector | 不加新组件,事务一致 |
| SaaS 产品(独立向量服务) | Qdrant | 高性能,过滤强,可自建 |
| 全托管 / 无运维能力 | Pinecone | 零运维,企业 SLA |
| 多模态搜索 | Weaviate | 内置多模态,Generative Search |
| 电商推荐(复杂过滤) | Qdrant | Filterable HNSW 最强 |
| 超大规模(10亿+) | Milvus | 分布式架构,水平扩展 |
| Edge / Serverless | LanceDB | 嵌入式,S3 原生 |
以下是一个生产级的向量搜索服务,抽象了底层引擎,支持运行时切换:
# vector_service.py — 统一向量搜索服务
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import List, Dict, Optional
import numpy as np
@dataclass
class VectorDocument:
id: str
text: str
vector: List[float]
metadata: Dict
@dataclass
class SearchResult:
id: str
text: str
score: float
metadata: Dict
class VectorStore(ABC):
"""向量存储抽象层"""
@abstractmethod
def upsert(self, docs: List[VectorDocument]) -> int: ...
@abstractmethod
def search(self, query_vector: List[float], top_k: int = 10,
filters: Optional[Dict] = None) -> List[SearchResult]: ...
@abstractmethod
def delete(self, ids: List[str]) -> int: ...
class QdrantStore(VectorStore):
"""Qdrant 实现 — 适合生产环境"""
def __init__(self, collection_name: str, dim: int = 1536):
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams
self.client = QdrantClient(host="localhost", port=6333)
self.collection = collection_name
# 自动创建 Collection
try:
self.client.create_collection(
collection_name=collection_name,
vectors_config=VectorParams(size=dim, distance=Distance.COSINE),
)
except Exception:
pass # Collection 已存在
def upsert(self, docs: List[VectorDocument]) -> int:
from qdrant_client.models import PointStruct
points = [
PointStruct(id=hash(d.id) % (2**63), vector=d.vector, payload={
"text": d.text, **d.metadata
})
for d in docs
]
self.client.upsert(self.collection, points)
return len(points)
def search(self, query_vector, top_k=10, filters=None) -> List[SearchResult]:
from qdrant_client.models import Filter, FieldCondition, MatchValue
qdrant_filter = None
if filters:
conditions = [
FieldCondition(key=k, match=MatchValue(value=v))
for k, v in filters.items()
]
qdrant_filter = Filter(must=conditions)
results = self.client.search(
collection_name=self.collection,
query_vector=query_vector,
limit=top_k,
query_filter=qdrant_filter,
)
return [
SearchResult(id=str(r.id), text=r.payload.get("text", ""),
score=r.score, metadata=r.payload)
for r in results
]
def delete(self, ids: List[str]) -> int:
self.client.delete(
collection_name=self.collection,
points_selector=[hash(id_) % (2**63) for id_ in ids],
)
return len(ids)
class LanceDBStore(VectorStore):
"""LanceDB 实现 — 适合开发/Edge"""
def __init__(self, db_path: str = "./vectordb", dim: int = 1536):
import lancedb
self.db = lancedb.connect(db_path)
self.dim = dim
def upsert(self, docs: List[VectorDocument], table_name: str = "default") -> int:
import pandas as pd
df = pd.DataFrame([{
"id": d.id, "text": d.text, "vector": d.vector, **d.metadata
} for d in docs])
try:
table = self.db.open_table(table_name)
table.add(df)
except Exception:
self.db.create_table(table_name, df)
return len(docs)
def search(self, query_vector, top_k=10, filters=None, table_name="default") -> List[SearchResult]:
table = self.db.open_table(table_name)
query = table.search(query_vector).limit(top_k)
if filters:
for k, v in filters.items():
query = query.where(f"{k} = '{v}'")
results = query.to_pandas()
return [
SearchResult(id=str(r["id"]), text=r["text"],
score=1 - r["_distance"], metadata={})
for _, r in results.iterrows()
]
def delete(self, ids: List[str], table_name: str = "default") -> int:
# LanceDB 通过 SQL 删除
table = self.db.open_table(table_name)
ids_str = ", ".join(f"'{i}'" for i in ids)
table.delete(f"id IN ({ids_str})")
return len(ids)
# 使用示例
def main():
# 开发环境用 LanceDB
store = LanceDBStore() if os.getenv("ENV") == "dev" else QdrantStore("production")
docs = [
VectorDocument("1", "RAG 系统需要向量数据库", [0.1]*1536, {"source": "tutorial"}),
VectorDocument("2", "LanceDB 是嵌入式向量数据库", [0.2]*1536, {"source": "docs"}),
]
store.upsert(docs)
results = store.search([0.15]*1536, top_k=5, filters={"source": "tutorial"})
for r in results:
print(f"[{r.score:.3f}] {r.text}")
纯向量搜索擅长语义匹配,但精确关键词(产品名、SKU、缩写)可能丢失。混合搜索是 2026 年的标准做法:
score_rrf = Σ 1/(k + rank_i)
# k=60 是常用值
score = α·score_vector + (1-α)·score_keywordwith_hybrid()| 方案 | 实现 | 隔离级别 | 性能影响 |
|---|---|---|---|
| Namespace | Pinecone | 逻辑 | 低 |
| Tenant Per Collection | Weaviate | 物理 | 高(租户多时) |
| Payload Filter | Qdrant / Milvus | 逻辑 | 低 |
| Partition Key | Milvus | 物理 | 低 |
| RLS | pgvector | 行级 | 中 |
向量数据库是 Agent 系统的"记忆层"。在 Agent 架构研究 中,多个项目展示了不同的向量存储策略:
| Agent 类型 | 推荐 | 理由 |
|---|---|---|
| 个人助手(本地运行) | LanceDB | 嵌入式、零运维、S3 备份 |
| 多用户 Agent SaaS | Qdrant | 过滤强、性能好、多租户 |
| 研究 Agent(大量文档) | Qdrant / Milvus | 大规模、混合搜索 |
| 对话 Agent(短期记忆) | pgvector | 与业务数据同库 |
| 嵌入式 Agent(IoT/Edge) | LanceDB | 无服务器、文件直存 |