起因
RAG 场景需要向量检索:
- embed 文档 chunk → 存向量
- query 时 embed query → 找最近的 K 个 chunk
- chunk 喂 LLM 做答
向量 DB 选择多:pgvector / Qdrant / Weaviate / Milvus / Pinecone /
LanceDB / Chroma。痛苦。
下面对比 + 我的选型建议。
候选
pgvector(Postgres 扩展)
CREATE EXTENSION vector;
CREATE TABLE chunks (
id BIGSERIAL PRIMARY KEY,
content TEXT,
embedding vector(1536)
);
CREATE INDEX ON chunks USING hnsw (embedding vector_cosine_ops);
-- 查
SELECT content
FROM chunks
ORDER BY embedding <=> '[0.1, 0.2, ...]'::vector
LIMIT 10;
- 已有 PG → 0 引入新组件
- 同事务支持 metadata + 向量
- 性能:HNSW index 几百万 vector OK,千万级别仍可(精度 vs 速度调)
Qdrant(Rust)
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct
client = QdrantClient('localhost', port=6333)
client.recreate_collection('docs',
vectors_config=VectorParams(size=1536, distance=Distance.COSINE))
client.upsert('docs', points=[
PointStruct(id=1, vector=[0.1]*1536, payload={'text': '...'}),
])
results = client.search('docs', query_vector=[0.1]*1536, limit=10,
query_filter={'must': [{'key': 'lang', 'match': {'value': 'zh'}}]})
- Rust 写,单机性能强
- payload 支持 filter(带 metadata 过滤)
- 部署简单(docker run 一行)
LanceDB
import lancedb
db = lancedb.connect('./lance_data')
tbl = db.create_table('docs', data=[
{'vector': [0.1]*1536, 'text': '...'},
])
results = tbl.search([0.1]*1536).limit(10).to_pandas()
- 嵌入式(无 server,类似 SQLite)
- 单 binary,无依赖
- 数据存 Lance 列式格式
- 适合 < 1 亿 vector / 不要分布式
Milvus
- 老牌(2019+)
- 真正分布式 + 集群(cloud-native)
- 适合数十亿 vector / 高 QPS
- 部署复杂
Chroma / Weaviate
- Chroma:embedded / server,开发体验最好
- Weaviate:Go 写,schema + GraphQL
- 我用得少
性能对比
10M vectors / 1536d / 测试:
| DB | 索引时间 | P50 query | RAM |
|---|---|---|---|
| pgvector (HNSW) | 30 min | 12 ms | 25 GB |
| Qdrant | 15 min | 5 ms | 18 GB |
| LanceDB | 10 min | 8 ms | 8 GB (disk-based) |
| Milvus | 12 min | 4 ms | 20 GB |
Qdrant / Milvus 在 raw 性能最强。pgvector 略慢但 ergonomics 最好。
选型建议
| 场景 | 推荐 |
|---|---|
| 已有 PG 应用 + < 1000w vector | pgvector |
| 独立向量服务 + 中等规模(千万级) | Qdrant |
| 嵌入应用 / 单机 / 不想跑 server | LanceDB |
| 数十亿规模 / 集群 | Milvus |
| 全托管不想运维 | Pinecone(贵)/ Qdrant Cloud / Pinecone serverless |
我个人项目 100% pgvector:
- DB 已经在
- transaction 跟其它 data 一致
- 不想多维护一个组件
- 性能够(< 100w vector)
pgvector 详细 + 优化
-- HNSW index(更适合高维)
CREATE INDEX ON chunks USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
-- 查询时 ef_search 控制精度/速度
SET hnsw.ef_search = 100;
-- 半精度向量(节省一半空间,损失小)
ALTER TABLE chunks ALTER COLUMN embedding TYPE halfvec(1536);
CREATE INDEX ON chunks USING hnsw (embedding halfvec_cosine_ops);
halfvec(pgvector 0.7+)省内存 50%,精度损失 1-2%。
filter + 向量混合查
-- 找符合 metadata + 最近向量
SELECT content
FROM chunks
WHERE lang = 'zh'
AND created_at > '2025-01-01'
ORDER BY embedding <=> $query LIMIT 10;
PG 查询计划器自动选 index。带 filter 时 HNSW + B-tree 配合(pg17+
更智能 prefilter)。
hybrid search
向量召回往往不如 keyword + 向量混合。
def hybrid_search(query, k=10):
# 1. BM25 (PG FTS)
bm25_results = pg.execute("""
SELECT id, ts_rank(...) AS score
FROM chunks WHERE chunk_tsv @@ to_tsquery(%s)
ORDER BY score DESC LIMIT 50
""", query)
# 2. 向量
vec_results = pg.execute("""
SELECT id, 1 - (embedding <=> %s::vector) AS score
FROM chunks
ORDER BY embedding <=> %s::vector LIMIT 50
""", [vec, vec])
# 3. RRF (Reciprocal Rank Fusion)
return rrf_merge(bm25_results, vec_results, k=k)
实际 RAG 效果显著提升。pgvector / Qdrant 都有内置 hybrid(不同程度)。
embedding model 选
- OpenAI text-embedding-3-small / large:好但要 API
- bge-m3 / bge-large-zh(BGE 系列):开源中英双语强
- nomic-embed-text-v1.5:开源,128 维 - 768 维可调
- e5-mistral-7b:高质量但贵
- multilingual-e5-large:100+ 语言
中文 RAG 我用 bge-m3,足够好 + 开源 + 本地 GPU 跑(~150 MB model)。
真实 case:知识库 RAG
我们一个内部知识库:
- 5 万文档 / 切 chunk 后 30 万段
- 用 bge-m3 embedding(1024 维)
- 存 pgvector
- query:BM25 + vector hybrid
部署:
- Postgres 16 + pgvector 0.7
- 单 16 GB RAM 服务器
- query P50: 50 ms(含 embed 模型推理)
成本:$50/月 server,对比 Pinecone $200+。
功能足够,没必要专门 vector DB。
chunk 策略
向量质量第一影响 chunk:
- 太大(> 1000 token):embedding 模糊,定位差
- 太小(< 100 token):上下文缺失
- 重叠(10-20%):边界 case
通用 500 token + 100 overlap。技术文档可能 800 + 150。
踩过的坑
-
pgvector index 建得慢:百万 vector 建 HNSW 30 分钟。
SET maintenance_work_mem = '2GB'加快。 -
embedding dimension 改了:换模型 → 维度变 → 老数据 schema 不
兼容。必须重新索引全部。 -
filter selectivity 低 + 向量查:query plan 选 seq scan vector
field(不走 HNSW)。SET enable_seqscan = off测试。 -
cosine vs dot:normalized vector 用 cosine OK;non-normalized
用 dot product。混 → 结果错。 -
vector 类型 cast:
'[0.1]'::vectorJSON-like 字符串。从
list[0.1, 0.2]转字符串小心 numpy float 序列化精度丢失。
登录后参与评论。