# 向量数据库 --- ## 数据库对比矩阵 | 特性 | 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=仅 BM25,1=仅向量 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 专家** - 异步客户端的实现模式