中小规模向量检索(百万级)优先选 pgvector,一个数据库搞定事务与检索;大规模、高召回率、水平扩展需求再考虑专用向量库。
如果你已经在跑 Postgres,且向量规模在百万级以下,pgvector 通常是正确的首选——少维护一套系统,过滤、连表、事务都在一起。当在 Postgres 内部进行向量搜索开始出现召回率下降、高查询量下延迟上升、或需要横向扩展及运维交接时,才应该考虑 Pinecone 或 Qdrant 这样的专用向量数据库。切换的错误理由是"大家都在用向量数据库"。
三种方案我都上线过检索功能,下面写的是我实际做决策的方式,不是从官网复制来的功能对比表。
这三个产品不是同一个类别,把它们当作可互换的是第一个错误。
pgvector 是一个 Postgres 扩展。它向一个你可能已经在用的数据库添加了向量列类型和近似最近邻索引(IVFFlat,以及更新版本中的 HNSW)。它是开源的,运行在你现有的 Postgres 实例内部。
Pinecone 是一个完全托管的、闭源的向量数据库,作为云服务交付。你不运行它,而是调用它的 API。它的无服务器模型将存储和计算分离,所以你不需要像老架构那样手动调整 pod 大小。
Qdrant 是一个用 Rust 编写的开源向量数据库。你可以自托管(Docker、Kubernetes)或使用 Qdrant Cloud。它的设计围绕向量和丰富的 payload 过滤作为一等公民。
核心认知:pgvector 是你已有数据库的一个功能;Pinecone 是你租用的服务;Qdrant 是一个你可以自己运行或租用(Qdrant Cloud)的系统。
对大多数起步的团队,够用。如果你的数据已经在 Postgres 里,把 embeddings 放在同一个数据库意味着检索查询可以按 tenant_id 过滤、关联 users 表、遵守同一个事务——不需要双写、不需要同步任务、不需要在凌晨两点排查"向量库为什么是旧数据"的事故。
一个最小化配置如下:
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE documents (
id bigserial PRIMARY KEY,
tenant_id bigint NOT NULL,
content text,
embedding vector(1536)
);
-- HNSW index for cosine distance
CREATE INDEX ON documents
USING hnsw (embedding vector_cosine_ops);
查询就是 SQL,所以你可以把元数据过滤和相似度放在一条语句里:
SELECT id, content
FROM documents
WHERE tenant_id = 42
ORDER BY embedding <=> $1 -- <=> is cosine distance
LIMIT 5;
它开始吃力的地方:在更高容量下,预过滤与索引的交互会变得重要。结合 ANN 索引的限制性 WHERE 子句会迫使 Postgres 过度扫描来填充 LIMIT,影响召回率或延迟,而调优 hnsw.ef_search 变成一个真正的技术活。另外,你也在和事务型工作负载共享 CPU 和内存——大量的 embedding 回填会与生产流量争抢资源。索引构建时间、VACUUM 行为和内存大小都需要你自己负责。
核心认知:pgvector 在向量搜索开始和 OLTP 工作负载争抢资源之前,在运维简易性和查询表达力上胜出。
说实话,触发点通常是三件事之一:规模超过单台 Postgres 机器舒适服务的范围、需要与主数据库隔离的高并发查询、或者团队希望搜索成为别人的运维问题。
Pinecone 的卖点是你永远不用操心索引。没有服务器、没有 VACUUM、没有 HNSW 参数暴露给你需要处理——你只管 upsert 和查询。对于没有数据库专家的小团队来说,这确实有价值。但代价是实实在在的锁定:闭源且仅限云端,没有自托管的退路,你的 embeddings 和元数据都在一个你无法自己运行的厂商手里。定价是按量计费,低容量时效率高,但随着增长需要仔细建模。
Qdrant 处于中间位置。你得到了一个专为向量设计的引擎,具有强大的元数据过滤、可降低内存的量化选项,以及自托管或使用其云服务的自由。代价是自托管意味着你又要面对一个状态化的分布式系统——分片、复制、备份、升级。你用 Postgres 运维换来了 Qdrant 运维,只有在向量工作负载确实需要一个专用系统时才划算。
从 Python 使用 Qdrant 很直接:
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct
client = QdrantClient(url="http://localhost:6333")
client.recreate_collection(
collection_name="documents",
vectors_config=VectorParams(size=1536, distance=Distance.COSINE),
)
client.upsert(
collection_name="documents",
points=[
PointStruct(id=1, vector=embedding, payload={"tenant_id": 42}),
],
)
hits = client.search(
collection_name="documents",
query_vector=query_embedding,
limit=5,
)
核心认知:专用向量数据库在搜索规模或隔离是一级需求时才能发挥价值——不是作为锦上添花。
把这个作为起点假设,然后用你自己的数据做基准测试——召回率和延迟高度依赖于维度数量、过滤选择性和索引参数,这些都是表格无法体现的。
迁移本身很少是难点——重新 embedding 并批量加载几百万个向量是一个批处理任务。真正的持续成本是你架构中的第二个系统:又多了一个需要监控、备份、保护安全、与真相源保持同步、在事故期间需要推理的东西。
这就是构建 vs 购买的 calculus。留在 pgvector 上会保持你的攻击面小,但也会封住你的上限。迁移到 Pinecone 可以用锁定和使用量账单(随成功而增长)为代价换取运维外包。迁移到自托管 Qdrant 让你保持控制和开源,但交给你一个需要运行的分布式数据库。没有免费的选项——只有适合你团队技能和你的工作负载真实形态的权衡。
核心认知:专用向量数据库的真正代价是持续的运维攻击面,不是一次性迁移。
如果你已经在跑 Postgres 且向量数量在百万级以下,从 pgvector 开始——你会更快上线、更少 debug。当你想让搜索成为一个托管 API 且可以接受闭源锁定以换取近乎零运维时,迁移到 Pinecone。当你需要一个专用、开源的引擎且要么有能力自托管、要么愿意为 Qdrant Cloud 付费时,选择 Qdrant。无论你倾向于哪个,在承诺之前用你自己的语料库和现实的过滤条件做基准测试——正确的答案与具体工作负载相关,且会随增长而变化。