首页归档标签关于
← 返回首页

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

#AI#向量数据库#选型

选向量库最容易踩的坑,是拿公开 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 之后就自动消失了。