向量数据库怎么选:一次不吹参数的选型思路

选向量库最容易踩的坑,是拿公开 benchmark 的 QPS 排名直接下结论。那些数字对应特定的数据集、维度和硬件,跟你的场景往往差着一个量级。更靠谱的顺序是:先算自己的数据规模,再列必须支持的查询形态,最后才是性能与运维。
一、第一步:算规模,别猜
def vector_bytes(n, dim, dtype_bytes=4):
return n * dim * dtype_bytes
n = vector_bytes(1_000_000, 1536) # 100 万条、1536 维、float32
print(n, 'bytes ≈', round(n / 1024**3, 2), 'GiB') # 6144000000 ≈ 5.72 GiB
这还只是裸向量。近似最近邻索引(HNSW 的图连接、IVF 的倒排列表)、原始文本、元数据、以及更新时产生的临时空间都要另算。经验上先按裸向量的 1.5~2 倍预留内存/磁盘,再根据实测收敛。
顺带把两个常被忽略的量一起算:
- 写入速率:每天新增多少条?是批量导入还是实时 upsert?
- 并发查询:QPS 峰值 × 期望延迟,决定要不要副本与横向扩展。
如果总量在几十万条以内、QPS 个位数,嵌入式方案(本地文件 + FAISS / sqlite 扩展)就完全够用,引入一套分布式服务纯属给自己加运维负担。
二、查询形态决定了能不能用
比「快不快」更早该确认的是「支不支持」。把真实查询列出来:
| 查询形态 | 选型时的影响 |
|---|---|
| 纯向量 top-k | 所有方案都支持,差异主要在索引参数 |
| 带元数据过滤(租户、时间、状态) | 需要确认 filter 与 ANN 的结合方式 |
| 关键词 + 向量混合检索 | 是否内置 BM25,还是要自己拼两套 |
| 精确重排 / 分页深翻 | 候选集大小、是否支持二次打分 |
| 高频删除与更新 | 索引是否需要重建,删除是否即时生效 |
过滤是最容易出问题的地方。部分实现的 post-filter 会先取 ANN top-k 再过滤,当过滤条件很严格时,剩下的结果可能寥寥无几;pre-filter 则可能影响索引效率或退化为暴力扫描。多租户场景尤其要拿自己的过滤比例实测一次,别只看文档里写了「支持过滤」。
三、索引参数:理解两个取舍
HNSW
efSearch ↑ → 召回率 ↑、延迟 ↑
M ↑ → 图更密、召回更稳、内存 ↑、构建更慢
IVF
nprobe ↑ → 召回率 ↑、延迟 ↑
nlist → 影响聚类粒度,需配合数据量调整
这两个旋钮的本质都是用延迟换召回率。调参时固定一个目标(例如「recall@10 ≥ 0.95 时 p95 延迟最小」),在你的真实数据上网格搜一遍,比抄别人的配置有效得多。
四、四类方案的大致定位
| 方案 | 适合 | 主要成本 |
|---|---|---|
| 嵌入式库(FAISS / sqlite 扩展) | 单机、百万级以内、离线批处理 | 无服务化,扩容靠自己 |
| 通用数据库 + 向量扩展 | 已经在用该数据库、要强事务与关联查询 | 大规模 ANN 性能需验证 |
| 专用向量数据库 | 亿级、高并发、需要分布式与混合检索 | 多一个要运维的有状态服务 |
| 云托管向量服务 | 团队小、不想管集群 | 费用、数据出境与网络延迟 |
没有「最好的」,只有「当前阶段最省事的」。一个务实的判断标准是:这个方案出问题时,团队里有没有人能在半小时内定位并恢复。
五、压测要用自己的数据
公开榜单通常不覆盖你的维度分布、过滤比例和长尾 query。最小可用的压测脚本长这样:
# 1) 灌入真实数据的抽样(含元数据分布)
# 2) 准备 200 条真实 query + 人工/暴力检索算出的 ground truth
# 3) 扫 efSearch / nprobe 组合
for ef in [32, 64, 128, 256]:
hits = index.search(q_vec, k=10, ef_search=ef)
print(ef, recall_at_k(hits, truth, 10), p95_latency(hits))
把结果画成「召回率—延迟」曲线,选曲线拐点,再把对应的参数写进配置并在版本里注明数据规模。数据涨了十倍之后要重跑,这一步经常被忘掉。
六、上线前检查清单
- 按公式算过裸向量与索引的空间,预留了余量
- 用真实过滤比例验证过召回,确认不是 post-filter 陷阱
- 混合检索方案确定,明确由谁负责融合与重排
- 删除/更新路径验证过,过期内容不会再被召回
- 索引重建、备份恢复演练过一次
- 数据规模增长到阈值时,有重跑压测的提醒
一句话收尾:先算清自己的数据和查询,再去看产品文档。很多选型纠结,在算完那行 n * dim * 4 之后就自动消失了。