Files
2026-07-13 21:36:42 +08:00

590 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 向量数据库
---
## 数据库对比矩阵
| 特性 | Pinecone | Weaviate | Qdrant | Chroma | pgvector |
|---------|----------|----------|--------|--------|----------|
| **托管方式** | 仅托管 | 托管 + 自托管 | 托管 + 自托管 | 自托管(云版内测中) | 自托管 |
| **混合搜索** | 支持(稀疏-稠密) | 支持(BM25 + 向量) | 支持(稀疏向量) | 有限 | 手动(+ pg_trgm |
| **过滤能力** | 优秀 | 优秀 | 优秀 | 基础 | SQL 原生 |
| **最大维度** | 20,000 | 无限 | 65,535 | 无限 | 2,000 |
| **定价模式** | 按向量/查询计费 | 按节点计费 | 按节点计费 | 免费(开源) | 免费(扩展) |
| **多租户** | 命名空间 | 多租户类 | 集合 + 负载 | 集合 | Schema/RLS |
| **最适合** | 企业级 SaaS | 语义应用 | 高性能场景 | 原型开发 | PostgreSQL 用户 |
## 何时使用各方案
### Pinecone
```
最适合:
- 有严格 SLA 的企业级 RAG
- 希望零基础设施管理的团队
- 需要稀疏-稠密混合搜索的应用
- 成本可预测的高并发生产环境
应避免:
- 成本敏感型项目(大规模时价格昂贵)
- 需要自托管或数据本地化
- 超出元数据的复杂过滤需求
- 希望避免厂商锁定
```
### Weaviate
```
最适合:
- 带内置向量化的语义搜索
- 多模态(文本、图像)应用
- GraphQL 原生团队
- 需要 BM25 + 向量混合搜索
应避免:
- 仅需简单嵌入存储
- 内存受限的环境
- 不熟悉 GraphQL 的团队
```
### Qdrant
```
最适合:
- 高性能、低延迟需求
- 带负载索引的复杂过滤
- Rust/性能优先团队
- 自托管,完全控制
应避免:
- 希望完全托管便捷性的团队
- 偏好 GraphQL(仅支持 REST/gRPC
```
### Chroma
```
最适合:
- 本地开发与原型验证
- LangChain/LlamaIndex 集成
- 简单 RAG 概念验证
- 教学项目
应避免:
- 大规模生产负载
- 多租户应用
- 高可用性需求
```
### pgvector
```
最适合:
- 已有 PostgreSQL 基础设施
- 事务 + 向量在同一数据库
- SQL 原生团队
- 成本优化(无需新增基础设施)
应避免:
- 向量维度超过 2000
- 数十亿级向量(扩展性限制)
- 亚毫秒级延迟需求
```
---
## Pinecone 设置
```python
from pinecone import Pinecone, ServerlessSpec
# 初始化客户端
pc = Pinecone(api_key="your-api-key")
# 创建无服务器索引
pc.create_index(
name="rag-index",
dimension=1536, # OpenAI ada-002
metric="cosine",
spec=ServerlessSpec(
cloud="aws",
region="us-east-1"
)
)
# 获取索引引用
index = pc.Index("rag-index")
# 上传向量及其元数据
index.upsert(
vectors=[
{
"id": "doc-1",
"values": embedding_vector,
"metadata": {
"source": "manual.pdf",
"page": 42,
"section": "installation",
"tenant_id": "acme-corp"
}
}
],
namespace="production"
)
# 带元数据过滤的查询
results = index.query(
vector=query_embedding,
top_k=10,
include_metadata=True,
namespace="production",
filter={
"tenant_id": {"$eq": "acme-corp"},
"section": {"$in": ["installation", "setup"]}
}
)
# 混合搜索(稀疏-稠密)
from pinecone_text.sparse import BM25Encoder
bm25 = BM25Encoder()
bm25.fit(corpus) # 在文档集上拟合
results = index.query(
vector=dense_embedding,
sparse_vector=bm25.encode_queries(query_text),
top_k=10,
alpha=0.5 # 平衡稠密与稀疏权重
)
```
---
## Weaviate 设置
```python
import weaviate
from weaviate.classes.config import Configure, Property, DataType
# 连接到 Weaviate Cloud
client = weaviate.connect_to_weaviate_cloud(
cluster_url="https://your-cluster.weaviate.network",
auth_credentials=weaviate.auth.AuthApiKey("your-api-key")
)
# 或自托管
client = weaviate.connect_to_local(
host="localhost",
port=8080
)
# 创建带向量化器的集合
client.collections.create(
name="Document",
vectorizer_config=Configure.Vectorizer.text2vec_openai(
model="text-embedding-3-small"
),
properties=[
Property(name="content", data_type=DataType.TEXT),
Property(name="source", data_type=DataType.TEXT),
Property(name="page", data_type=DataType.INT),
Property(name="tenant_id", data_type=DataType.TEXT, index_filterable=True)
]
)
# 自动向量化插入
documents = client.collections.get("Document")
documents.data.insert(
properties={
"content": "Installation guide content...",
"source": "manual.pdf",
"page": 42,
"tenant_id": "acme-corp"
}
)
# 或使用预计算向量
documents.data.insert(
properties={"content": "...", "source": "..."},
vector=precomputed_embedding
)
# 混合搜索(BM25 + 向量)
from weaviate.classes.query import MetadataQuery, Filter
results = documents.query.hybrid(
query="how to install",
alpha=0.5, # 0=仅 BM251=仅向量
limit=10,
filters=Filter.by_property("tenant_id").equal("acme-corp"),
return_metadata=MetadataQuery(score=True, explain_score=True)
)
for obj in results.objects:
print(f"Score: {obj.metadata.score}, Content: {obj.properties['content'][:100]}")
client.close()
```
---
## Qdrant 设置
```python
from qdrant_client import QdrantClient
from qdrant_client.models import (
Distance, VectorParams, PointStruct,
Filter, FieldCondition, MatchValue,
PayloadSchemaType
)
# 连接到 Qdrant Cloud
client = QdrantClient(
url="https://your-cluster.qdrant.io",
api_key="your-api-key"
)
# 或本地连接
client = QdrantClient(host="localhost", port=6333)
# 创建集合
client.create_collection(
collection_name="documents",
vectors_config=VectorParams(
size=1536,
distance=Distance.COSINE
)
)
# 创建负载索引以实现快速过滤
client.create_payload_index(
collection_name="documents",
field_name="tenant_id",
field_schema=PayloadSchemaType.KEYWORD
)
# 上传数据点
client.upsert(
collection_name="documents",
points=[
PointStruct(
id="doc-1",
vector=embedding_vector,
payload={
"content": "Installation guide...",
"source": "manual.pdf",
"page": 42,
"tenant_id": "acme-corp"
}
)
]
)
# 带过滤条件的搜索
results = client.search(
collection_name="documents",
query_vector=query_embedding,
limit=10,
query_filter=Filter(
must=[
FieldCondition(
key="tenant_id",
match=MatchValue(value="acme-corp")
)
]
),
with_payload=True
)
# 大批量数据上传
from qdrant_client.models import Batch
client.upsert(
collection_name="documents",
points=Batch(
ids=ids_list,
vectors=vectors_list,
payloads=payloads_list
)
)
```
---
## Chroma 设置
```python
import chromadb
from chromadb.config import Settings
# 持久化本地存储
client = chromadb.PersistentClient(
path="./chroma_data",
settings=Settings(anonymized_telemetry=False)
)
# 使用自定义嵌入函数创建集合
from chromadb.utils.embedding_functions import OpenAIEmbeddingFunction
embedding_fn = OpenAIEmbeddingFunction(
api_key="your-openai-key",
model_name="text-embedding-3-small"
)
collection = client.get_or_create_collection(
name="documents",
embedding_function=embedding_fn,
metadata={"hnsw:space": "cosine"}
)
# 添加文档(自动嵌入)
collection.add(
ids=["doc-1", "doc-2"],
documents=["Installation guide...", "Configuration steps..."],
metadatas=[
{"source": "manual.pdf", "page": 42},
{"source": "manual.pdf", "page": 43}
]
)
# 或使用预计算嵌入
collection.add(
ids=["doc-3"],
embeddings=[precomputed_vector],
metadatas=[{"source": "guide.pdf"}],
documents=["Original text for reference"]
)
# 查询
results = collection.query(
query_texts=["how to install"],
n_results=10,
where={"source": "manual.pdf"},
include=["documents", "metadatas", "distances"]
)
# 更新已有文档
collection.update(
ids=["doc-1"],
documents=["Updated installation guide..."],
metadatas=[{"source": "manual_v2.pdf", "page": 42}]
)
```
---
## pgvector 设置
```sql
-- 启用扩展
CREATE EXTENSION IF NOT EXISTS vector;
-- 创建含向量列的表
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
content TEXT NOT NULL,
embedding vector(1536), -- OpenAI 维度
source VARCHAR(255),
page INTEGER,
tenant_id VARCHAR(100),
created_at TIMESTAMP DEFAULT NOW()
);
-- 创建 HNSW 索引(大多数情况下推荐)
CREATE INDEX ON documents
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
-- 或针对超大数据集使用 IVFFlat
CREATE INDEX ON documents
USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 100);
-- 在过滤列上创建索引
CREATE INDEX ON documents (tenant_id);
```
```python
import psycopg2
from pgvector.psycopg2 import register_vector
conn = psycopg2.connect("postgresql://localhost/ragdb")
register_vector(conn)
# 插入嵌入向量
cur = conn.cursor()
cur.execute(
"""
INSERT INTO documents (content, embedding, source, page, tenant_id)
VALUES (%s, %s, %s, %s, %s)
RETURNING id
""",
("Installation guide...", embedding_vector, "manual.pdf", 42, "acme-corp")
)
# 带过滤条件的相似度搜索
cur.execute(
"""
SELECT id, content, source, page,
1 - (embedding <=> %s) AS similarity
FROM documents
WHERE tenant_id = %s
ORDER BY embedding <=> %s
LIMIT 10
""",
(query_embedding, "acme-corp", query_embedding)
)
results = cur.fetchall()
# 使用 pg_trgm 进行混合搜索
cur.execute(
"""
SELECT id, content,
(0.5 * (1 - (embedding <=> %s))) +
(0.5 * similarity(content, %s)) AS hybrid_score
FROM documents
WHERE tenant_id = %s
AND content %% %s -- Trigram 相似度阈值
ORDER BY hybrid_score DESC
LIMIT 10
""",
(query_embedding, query_text, "acme-corp", query_text)
)
```
---
## 索引调优指南
### HNSW 参数
| 参数 | 描述 | 权衡 |
|-----------|-------------|-----------|
| `m` | 每个节点的连接数 | 越大 = 召回率越高,内存占用越大 |
| `ef_construction` | 构建时的搜索宽度 | 越大 = 索引质量越好,构建越慢 |
| `ef_search` | 查询时的搜索宽度 | 越大 = 召回率越高,查询越慢 |
```python
# Qdrant HNSW 调优
client.update_collection(
collection_name="documents",
hnsw_config=HnswConfigDiff(
m=16, # 默认值:16,提高召回率可增大
ef_construct=100, # 默认值:100,提高索引质量可增大
full_scan_threshold=10000 # 低于此大小时使用暴力搜索
)
)
# 查询时 ef 调整
results = client.search(
collection_name="documents",
query_vector=query_embedding,
limit=10,
search_params=SearchParams(hnsw_ef=128) # 越大召回率越高
)
```
### 量化扩展
```python
# Qdrant 标量量化(内存减少 4 倍)
from qdrant_client.models import ScalarQuantization, ScalarQuantizationConfig
client.update_collection(
collection_name="documents",
quantization_config=ScalarQuantization(
scalar=ScalarQuantizationConfig(
type="int8",
quantile=0.99,
always_ram=True
)
)
)
```
---
## 多租户模式
### 命名空间隔离(Pinecone
```python
# 不同命名空间中的租户数据
index.upsert(vectors=[...], namespace="tenant-acme")
index.upsert(vectors=[...], namespace="tenant-globex")
# 在租户命名空间内查询
results = index.query(
vector=query_embedding,
namespace="tenant-acme",
top_k=10
)
```
### 元数据过滤(Qdrant/Weaviate
```python
# 为所有文档添加 tenant_id
point = PointStruct(
id="doc-1",
vector=embedding,
payload={"tenant_id": "acme", "content": "..."}
)
# 始终按租户过滤
results = client.search(
collection_name="documents",
query_vector=query_embedding,
query_filter=Filter(
must=[FieldCondition(key="tenant_id", match=MatchValue(value="acme"))]
)
)
```
### 每租户独立集合(高隔离性)
```python
# 创建租户专属集合
client.create_collection(
collection_name=f"docs_{tenant_id}",
vectors_config=VectorParams(size=1536, distance=Distance.COSINE)
)
```
---
## 决策流程图
```
开始
├─ 需要托管服务且零运维?
│ └─ 是 → Pinecone
├─ 已有 PostgreSQL
│ └─ 是 → pgvector(若向量维度 < 2000
├─ 需要内置向量化?
│ └─ 是 → Weaviate
├─ 需要极致性能 + 自托管?
│ └─ 是 → Qdrant
├─ 原型开发 / 本地开发?
│ └─ 是 → Chroma
└─ 默认推荐 → Qdrant(功能与性能的平衡)
```
---
## 快速参考
| 任务 | Pinecone | Weaviate | Qdrant | pgvector |
|------|----------|----------|--------|----------|
| 创建索引/集合 | `create_index()` | `collections.create()` | `create_collection()` | `CREATE TABLE` |
| 插入 | `upsert()` | `data.insert()` | `upsert()` | `INSERT` |
| 搜索 | `query()` | `query.near_vector()` | `search()` | `ORDER BY <=>` |
| 过滤 | `filter={}` | `Filter.by_property()` | `query_filter=Filter()` | `WHERE` |
| 删除 | `delete()` | `data.delete_by_id()` | `delete()` | `DELETE` |
| 混合搜索 | sparse_vector 参数 | `query.hybrid()` | sparse vectors | 手动实现 |
## 相关技能
- **数据库优化器** - 索引调优与查询性能
- **云架构师** - 向量数据库托管的基础设施决策
- **Python 专家** - 异步客户端的实现模式