官方 benchmark 通常跑在理想数据集和固定参数下,而你的 RAG 场景有独特的向量维度、过滤比例和并发模式。直接看榜单选型,上线后往往发现延迟或召回不达预期。一套可控的本地基准测试,比任何第三方报告都更能回答“哪个数据库适合我”。
先定义你要测什么
向量数据库选型不是单一指标排序,而是多维权衡。建议至少覆盖以下可测量维度:
- 索引构建耗时:从原始向量写入到索引可查询的时间。HNSW 类索引构建慢但查询快,IVF 类构建快但需要训练阶段。
- 查询延迟随数据规模的变化:在 10 万、100 万、1000 万向量下分别测 P50、P95、P99 延迟,观察增长曲线是线性还是亚线性。
- 过滤条件对召回的影响:带标量过滤的向量检索,实际召回率往往低于纯向量检索。需要测不同过滤比例(1%、10%、50%)下的召回变化。
- 内存与磁盘占用:索引文件大小、进程 RSS、是否支持 mmap 或磁盘索引。
- 召回率与延迟的权衡:调整 efSearch、nprobe 等参数,画出召回-延迟曲线,而不是只测一个点。
生成合成向量数据
不要用随机均匀分布向量,它不能反映真实 embedding 的聚类特性。推荐用 sklearn 的 make_blobs 生成带簇结构的向量:
from sklearn.datasets import make_blobs
import numpy as np
n, dim, centers = 1_000_000, 768, 100
X, labels = make_blobs(n_samples=n, n_features=dim,
centers=centers, cluster_std=0.3,
random_state=42)
X = X.astype(np.float32)
# 归一化,模拟 cosine 相似度场景
X /= np.linalg.norm(X, axis=1, keepdims=True)
np.save("vectors_1m.npy", X)
同时生成标量字段用于过滤测试:category 取 0-99,timestamp 取递增整数。查询集从同一分布中采样 1000 条,并记录每条的真实最近邻(用暴力检索算 ground truth),用于计算召回率。
环境隔离与变量控制
基准测试最大的敌人是环境噪声。建议:
- 固定 CPU 核数(
taskset -c 0-7),避免调度抖动。 - 关闭 swap,或监控 swap 使用量。
- 每个数据库单独容器运行,限制内存上限一致。
- 预热:先跑 1000 次查询丢弃,再开始计时。
- 每项测试重复 3 次,取中位数,记录标准差。
编写对比脚本
统一接口是关键。为每个数据库写一个 adapter,暴露 build_index(vectors, metadata)、query(vector, top_k, filter_expr)、stats() 三个方法。主脚本按以下流程执行:
for scale in [100_000, 1_000_000, 10_000_000]:
vectors = load_vectors(scale)
for db in databases:
t0 = time.perf_counter()
db.build_index(vectors, metadata)
build_time = time.perf_counter() - t0
latencies, recalls = [], []
for q, truth in zip(queries, ground_truths):
t1 = time.perf_counter()
results = db.query(q, top_k=10)
latencies.append(time.perf_counter() - t1)
recalls.append(len(set(results) & set(truth[:10])) / 10)
record(db.name, scale, build_time, latencies, recalls, db.stats())
过滤测试单独一轮:在 query 中传入 filter_expr,对比无过滤与有过滤的召回率和延迟差异。注意有些数据库在过滤比例高时会退化为暴力扫描,延迟曲线会出现拐点。
结果解读与常见陷阱
拿到数据后,不要只看平均值。重点看:
- P99 延迟:RAG 在线服务对尾延迟敏感,P99 可能是 P50 的 5-10 倍。
- 召回率下降幅度:过滤比例从 1% 升到 50% 时,召回率掉了多少。如果掉超过 10 个百分点,需要评估是否可接受。
- 内存增长斜率:向量数翻倍时内存是否也翻倍。支持量化或磁盘索引的数据库斜率更平缓。
- 构建时间与查询延迟的权衡:构建 1 小时但查询快 3 倍,是否值得取决于你的索引更新频率。
常见陷阱包括:用随机向量导致召回虚高、忽略预热导致首查延迟污染、过滤字段未建索引导致全表扫描、以及在不同并发下测延迟却未标注并发数。
可执行建议
先明确你的硬约束:数据规模上限、可接受 P99 延迟、过滤比例分布、索引更新频率。然后用上述方法跑一轮,把结果画成召回-延迟曲线和规模-延迟曲线。选型决策应该基于曲线在你目标工作点的表现,而不是单一 benchmark 分数。最后,把测试脚本纳入 CI,每次升级数据库版本后重跑,防止性能回归。