求求你了,别再用随机向量测试向量数据库了

发布时间:2026/10/11 17:33:43
求求你了,别再用随机向量测试向量数据库了
想测一个向量数据库最省事的办法是什么写几行脚本随机生成一批向量灌进去建索引再发一批查询。数据量对了维度对了参数也对齐了看起来挺公平。接下来看到召回率上不去就开始调参数调完还是不理想心里可能已经有了判断这个数据库的向量检索不太行。先别急。你刚刚测到的搜索难度和业务里的文本检索是一回事吗这次在 seekdb 上做的对照实验就从这个问题开始。数据库、数据规模、向量维度、建图参数都保持一致只换向量的来源。结果召回曲线差了很远。先说清楚这三组向量各自想回答什么真实文本向量贴近这次要测的检索场景。把公开问答数据中的段落和问题交给同一个文本编码模型变成向量。查询来自真实问题库里放真实段落保留问题与文本之间原有的联系。这一组回答在这份文本检索负载上索引能找回多少精确近邻随机生成的向量看看常见的“随手造数据”测出了什么。直接随机生成一批数再把每条向量的长度统一条数和维度与真实组对齐。查询也单独随机生成。这一组方便、可重复适合做合成测试。放在这里是为了检验它能否代替上面的真实文本负载。打乱结构的向量再追问一步真实数据里的数值关系重要吗从真实向量出发把每个位置上的数字分别在不同向量之间打乱再统一长度。原来属于同一条文本的那些数字被拆散了查询也单独这样处理。这样做是想观察保留一部分数值分布特征、破坏原有组合关系后结果会怎样所以这里既有真实负载也有两个目的不同的对照。具体怎么生成、怎么打乱放在文末先看结果。同样的搜索参数找回来的差这么多先把指标翻译成人话先在数据库外用穷举得到每个查询最相近的 10 条向量再检查近似索引找回了其中多少。这个比例就是下面的ANN Recall10。每组都和自己的精确答案比较。图 1seekdb 的同一套索引配置搜索参数 ef_search 固定为 80。柱越长找回的精确近邻越多细误差棒为 95% bootstrap 区间。这里评估近邻查找不是问答正确率。真实文本向量找回了98.03%随机生成的向量是24.11%打乱结构的向量是29.13%。只看随机组很容易得出“这个参数下召回很差”的印象。把同样的参数用到这份真实文本数据上看到的却是另一条曲线。这三个结果都是真实测量。问题在于只把条数、维度和索引参数对齐还不能让随机数据代表真实业务。而且这个差距并没有伴随同样悬殊的单次延迟。在这个参数点三组的客户端 p95 分别是 0.640、0.698、0.733 毫秒。p95 可以理解为约 95% 的查询在这个时间以内完成。具体计时范围见附录。为什么换了数据索引就像换了一道题想象一下你在找一段讨论数据库索引的文字。文本编码模型的目标是让意思相关的文本在向量空间里更接近。具体能做到什么程度取决于模型和语料但这些向量之间的关系毕竟来自真实的语言内容。随机生成一堆数没有复制这层关系。即便维度、长度和数据量一样“谁和谁挨着”“查询附近的候选有多难区分”也可能完全不同。HNSW 会沿着近邻图逐步寻找更接近查询的位置因此数据的邻接关系会影响搜索。可以把它理解为路线图变了同一个搜索设置面对的路况也变了。关于算法本身可参见 HNSW 原论文。这次还检查了真实数据里的近邻关系真实组中查询与最近邻的平均 cosine 相似度更高排在第 10、11 位的邻居之间差距也更大。换句话说这份数据中最靠近查询的那些点更突出前 10 名的边界更容易区分。打乱结构后均值向量的长度与真实组仍然很接近近邻关系和召回表现却明显变了。这提醒我们光看几个数值统计未必能看出完整的搜索难度。这些检查给出了和结果相符的线索。不过我们没有测量搜索轨迹、内在维度或聚类程度不能宣称“已经证明某一种几何性质导致了全部差距”。完整诊断值放在附录。多找一会儿能把差距补回来吗前面只看了一个参数点。选型时还得看整条曲线。把 ef_search 当作一个控制搜索范围的旋钮就好通常调大以后搜索更充分也会增加开销。它不是“实际访问了多少个点”的计数不同数据在同一个设置下也不一定做同样多的工作。图 2横轴是搜索参数从左到右逐步调大。上图越高精确近邻找回得越全下图越低客户端 p95 延迟越小。三组各测了 7 个参数点误差棒为 95% bootstrap 区间。随着搜索范围扩大三组召回率都在提高但到达同一个目标所需的条件不同。以95% 近邻召回为目标真实组在这次测过的参数点里首次达标的是 ef_search80。前一个点 ef_search40 是 94.79%。两个对照一路调到 ef_search640仍然没有过线随机组为 80.94%打乱组为 84.31%。同一参数下真实组是 99.97%。再把速度和召回放到一起看图 3越靠左延迟越低越靠上近邻找回得越全。每个点都是实测结果横纵误差棒对应延迟与召回的 95% 区间。连线只连接已测点没有推算未测参数。这也决定了本次实验不能给出一个很诱人的说法“达到同样召回率快了多少倍。”因为两个对照根本还没有达到 95%。要比较相同召回目标下的速度就得继续扩大搜索范围或调整建图参数让各组先到达目标。没达标就写没达标。下次测试随机数据可以留真实负载要补上随机向量有它很合适的位置检查接口能不能跑通、维度是否正确、索引是否生效做可重复的回归测试或专门构造压力与边界场景。明确告诉读者“这是某一种合成负载”这些结果就有清楚的用途。但如果你要据此选数据库、做容量规划建议至少再补上这些步骤带上真实查询和真实语料。尽量接近目标业务保留两者的关系记录编码模型和预处理方式。先确认索引准备好了。异步建索引时写入成功不等于索引已经可测同时检查状态和查询计划。把召回曲线和延迟曲线一起画出来。先设业务能接受的召回目标再比较达标配置的开销保留没有达标的点。逐步加回业务条件。过滤、正文 payload、并发、更新、网络和缓存状态都要在真实部署条件下继续测。这次实验的范围也很具体一份英文问答语料、一个模型、一种 HNSW 配置、一台机器上的静态小规模数据。它没有证明“真实向量总比随机向量好搜”也不能直接替你预测另一个业务。它说明的是随机向量测出来很难搜不能直接推断你的真实文本也一样难搜。下次看到一张向量数据库跑分图除了问“多少条、多少维、什么参数”再多问一句这些向量和我要搜的东西有多像复现附录1. 数据和对照怎么做三组都是30,000 条语料向量、384 维、500 个正式查询另有 100 个查询用于预热和 pilot。向量为 float32使用 cosine 距离。真实组从 HotpotQA dev distractor 上下文中抽取 30,000 个去重段落。先保留上述 600 个问题的 1,196 个支持段落再从其余段落中按固定种子补足。标题和正文一起用 all-MiniLM-L6-v2 编码再做 L2 归一化也就是把向量的整体长度统一。数据背景见 HotpotQA 原论文 和 官方数据与许可模型信息见 固定版本模型卡。随机组为语料和查询分别生成独立高斯随机向量再做 L2 归一化。这就是正文所说的“随机生成的向量”对应证据数据里的gaussian。打乱组对真实矩阵的每一列独立打乱行次序语料与查询分开处理再做 L2 归一化对应shuffled。这会打散跨坐标的联合结构如果仅对所有向量做同一个坐标置换cosine 距离完全不变起不到本次对照的作用。打乱当下每列的经验边际分布不变重新归一化后各分量会再变化不能声称最终精确保留了每维边际。这是构造的同分布问答负载支持段落被有意保留。查询与语料没有文本完全重合但不能据此把它称为跨领域测试。模型训练数据与公开数据的潜在重叠也无法排除。2. 数据库和机器配置项目本次实际配置引擎seekdb SERVER 1.4.0.0HNSW / VSAG连接与表PyMySQL本机回环连接表中仅有 ID 和向量没有正文 payload、过滤或重排建图参数M16ef_construction256搜索参数ef_search10、20、40、80、160、320、640截断1,147 / 30,000 段落超过 256 token3.8233%600 个查询均未超长编码依赖sentence-transformers 5.1.1transformers 4.56.2torch 2.8.0cpu随机种子20261010派生种子和插入次序见脚本未控制引擎内部所有随机性3. 开始计时前先排除哪些假差异确认异步索引就绪。每次装载后执行CALL dbms_index_manager.refresh()再检查索引计数与表行数一致、is_complete1、sync_fail_cnt0并保留 DDL、索引信息和等待时间。表行数达到 30,000 不单独构成通过条件。官方接口对阻塞刷新行为和版本要求的说明见 refresh_index 文档。确认存进去的向量没变。全量回读实际存储的 float32 分量与准备阶段数组逐值比对。为避免默认字符串输出舍入通过 ARRAY_MAP 将分量转换为 DOUBLE 后导出。独立计算精确答案。在数据库外穷举排名float64 累加并显式处理范数不把 float32 归一化后的范数视为精确的 1。每个分布、每次构建再抽查 10 个查询验证数据库精确 SQL 与独立真值一致。确认查询真的走了正确路径。ANN 计划必须出现 idx_vec 的 VECTOR INDEX SCAN精确 SQL 不能走该 ANN 路径。每次设置 ef_search 后回读生效值。正式记录中的 324 次 ANN 计划检查、9 次精确计划检查、324 次 ef 回读均通过返回数量、重复 ID 和范围检查也通过。4. 召回、重复测量与置信区间每组都与自己的精确 top-10 比较。主指标允许 cosine 差值不超过 10⁻⁷ 的边界并列tie-aware并将并列得分限制在剩余名额内避免错误补偿遗漏的更优近邻同时保留严格 ID Recall。ef_search80 时两种口径在三组中相同。索引重新构建三次各次使用新的插入顺序同一次构建内三组使用相同顺序。每次构建测五轮分布、ef_search 和轮次按块交错块内查询顺序随机每块先用独立的 100 个问题预热。共 315 个测量块、157,500 条查询记录不同的评测问题只有 500 个。图中的 95% 区间对构建与查询簇重采样保留簇内全部五轮bootstrap 1,000 次。真实组在 ef_search80 的 Recall 区间为 97.60%–98.43%三次构建均值范围为 97.94%–98.10%。只有三次构建区间对建图随机性的估计仍有限也不覆盖更换语料、模型或机器的不确定性。