向量数据库原理与实现

接入与开发

讲透向量数据库的索引原理与工程落地做法:索引怎么选、参数怎么调、召回怎么验、超时并发降级日志评测该配什么。

很多人接完 embedding(嵌入)接口、把向量写进库,就以为 RAG(检索增强生成)成了。真跑起来才发现:数据一多查询变慢,召回忽好忽坏,参数改完不知道有没有变好,线上偶尔超时还没有日志可查。这篇不重复向量和索引的概念,只给工程视角的判断标准、参数清单和排查顺序,让你把向量检索这一层做成能上线、能观测、能回归的模块。

很多人接完嵌入接口、把向量写进库,就以为检索这层成了。真跑起来才发现三个问题叠在一起:数据一多查询变慢,召回忽好忽坏,参数改完也不知道到底有没有变好。线上偶尔超时,翻日志还找不到是查询慢还是连接慢。

这篇不讲向量是什么、索引是什么概念,只讲工程落地:索引按什么分档选、参数按什么顺序调、召回怎么留回归集、运行时超时并发降级日志配哪几项。整套做法只有一句话,让向量检索成为能上线、能观测、能回滚的一层,而不是一个改不动也说不清的黑盒。

索引选型:先按数据量和延迟要求分档

选索引之前先量三个数:向量总条数、向量维度、单次查询能容忍的延迟。这三个数决定了你该走哪一档。数据量小的时候,暴力遍历往往比建索引更省事,延迟也不难看;条数上去以后,索引才真正开始有价值。顺序别反。到 /stacks/vector-dbs 可以对照各家产品支持的索引类型和写入特性。

场景特征优先考虑主要代价
条数少、维度不高、查询频率低暴力检索,先不建索引条数涨上来后延迟线性上升
查询延迟敏感、数据变动不频繁图类索引建索引慢、内存占用偏高
写入频繁、内存预算紧聚类倒排类索引召回略打折,要靠多探簇补
数据日更、要求边写边查支持增量写入的索引部分实现需要重建,要提前确认

把「索引建好」当成第一版而不是终点。上线后拿真实查询分布回看参数,比一开始纠结选哪种索引有效得多。有两类情况不适合照上面的档走:一是查询量极低、几天才查一次的内部工具,直接暴力遍历就行;二是向量条数很少但单条维度极高,内存和计算压力可能比条数更早成为瓶颈。

  • 先量条数、维度、可容忍延迟三个数,再定索引类型,不要凭感觉选
  • 写入频繁的项目单独查一遍:该索引是每次新增都重建,还是支持边写边查
  • 把索引当作第一版,上线后按真实查询分布再回看参数
  • 数据量小、查询频率低的场景直接用暴力检索,别为了建索引而建索引

参数调优:每次只动一个,动完必须回测

向量检索的参数看着多,其实可以分两组:一组影响召回,比如搜多少候选、探多少个簇;一组影响性能和写入,比如分片、副本、量化压缩。这两组一定要分开调,同时改就说不清是谁在起作用。建议顺序是先把召回质量固定达标,再压延迟,最后才考虑省内存。反过来做很容易先把召回压坏,再花时间回头救。

  1. 分组先把手头所有参数归到召回组或性能组,写在一张纸上,避免调着调着混在一起。
  2. 固定召回只动召回组参数,跑到小评测集上的命中结果稳定达标为止,记录当时的配置。
  3. 再压延迟召回达标后,再动分片、量化这类性能参数,跑同一套评测集对比延迟变化,确认召回没有掉下去。
  4. 最后省内存内存吃紧时再考虑压缩类参数,压完必须重跑评测集,别默认压缩对召回没影响。
  5. 留记录每次改动记三件事:改了什么、在哪个数据集上、召回和延迟各变成多少。没有这条记录,下次出问题只能靠猜。

召回验证:留 20 条真实问题当回归集

召回没法靠感觉判断,得有一个固定的小评测集。做法是从真实业务里挑二十条左右的问题,人工标出每道题的正确答案应该来自哪几段内容,固化下来。之后每次改索引、换嵌入模型、动切片策略,都拿它跑一遍。这个集合不用大,关键是要真实、要固定、要能重复跑。

指标看什么不适用的情况
命中率正确片段有没有被捞回来只有一条标准答案、业务本身就不确定时参考价值有限
命中位置正确片段排在前几还是垫底对话式检索里位置要求可以放宽
噪声比例捞回的不相关片段会不会把上下文挤爆短上下文场景噪声容忍度更低,需要单独看
改动风险等级换嵌入模型属高风险,换索引参数属中低风险任何改动都不能只看单次结果就下结论

换嵌入模型属于高风险改动,必须走完整回测;只调索引参数可以只跑子集,但一定要有基线可比。检索质量下滑时,排查顺序是先怀疑进料,也就是切片、清洗、去重,再怀疑索引本身。多数召回问题其实是切片切坏了。切片与清洗要点可以到 /guides/rag-kb-build-pipeline 和 /guides/pdf-ocr-doc-cleaning 对照流程。噪声把上下文挤爆的问题,参考 /guides/context-budget-engineering 的做法。

运行时:超时、并发、降级与日志四件事

前面三节决定检索准不准,这一节决定它稳不稳。超时要按层设:连接超时、查询超时、整体接口超时分开配,别只给一个总数。向量查询慢的时候,要能一眼看出是连接层拖的还是查询层拖的。分层设置的思路可以到 /guides/api-timeout-tuning 对照。

并发要跟写入错开。批量灌数据和线上查询抢资源是很常见的卡点,灌库放在低峰、限制批大小,配合排队和失败重投。这套清单可以到 /guides/batch-concurrency-throttle-guide 看。降级路径要提前定好三条:向量库挂了或超时,是退回全文检索、退回缓存、还是直接少给几条结果,每种都要有明确的触发条件。混合检索的选型对照在 /guides/vector-fulltext-hybrid-search。

  • 日志至少记:查询向量来源、候选数量、最终命中条数、耗时、走了哪条降级路径
  • 没有这几项,线上「搜不到」的投诉基本没法复盘,只能靠猜
  • 降级触发条件写死在配置里,不要等出事现场再决定
  • 超时按层配,连接、查询、接口分开,方便定位是哪一层慢
下一步索引类型和参数没有放之四海皆准的答案,各家产品的支持范围、写入特性和实测表现差异不小,去 /stacks/vector-dbs 对照目录挑两三个候选,再拿你自己那二十条回归集跑一遍,比看任何评测结论都直接。

常见问题

向量数据库一定要单独部署一套吗?

不一定。向量条数在几千到几万、查询不频繁时,用普通数据库的向量扩展甚至内存里算都能撑住。判断标准是数据量大不大、查询 QPS(每秒查询数)高不高、能不能接受把检索和业务库绑在一起。规模上来或需要独立扩容时再拆,具体产品到 /stacks/vector-dbs 看目录。

检索结果不稳定,一会儿准一会儿不准,从哪查?

按这个顺序:先看进料有没有重复和脏数据,再看切片是不是把一段完整意思切断了,然后确认查询时用的嵌入模型和入库时是不是同一个,最后才是索引参数。前三条占大多数。如果只是偶发,重点看是不是超时导致候选变少,或者并发高峰时被限流降级了。

怎么判断一次索引或模型改动到底有没有变好?

留一个固定的小回归集,改动前后各跑一遍,比召回位置和噪声比例,不要凭感觉。只跑一两条问题容易得出相反结论。如果改动同时动了嵌入模型和切片,建议分两次上,否则出了问题没法定位是哪一步导致的。

本文依据各平台公开文档与官方公告整理,只做中文归纳;具体做法、参数与限制以官方文档为准。