向量检索最佳实践:原理、相似度度量与ANN索引选型
大家好,我是数据库小学妹👋 我踩过的坑,你别再踩。
刚转行那会儿,我在知识库里搜"怎么申请年假",文档标题写的是"休假流程说明",关键词一个都没对上,搜了个空。当时我以为是自己关键词没选好,后来我才慢慢明白,问题不在我,在检索方式本身。关键词检索要的是字面对上,而人问问题时,用的是意思。
向量检索,是把文本、图片这些内容转成高维空间里的向量,用距离或夹角衡量"意思像不像",返回语义最接近的若干条结果。哪怕查询里一个原词都没出现,它也能召回意思相近的内容。这篇不堆术语,把它的完整链路从Embedding到ANN,一步步讲清楚。
一、关键词检索卡在哪
先说清楚它解决什么。传统关键词检索靠倒排索引,把词和文档对起来,查询时匹配词。它有两个好处:字面匹配准,可解释性强,所以到现在还是检索系统的基础。但它有两个天生的短板。
一是同义不同词。“数据库备份"和"数据快照”,字面毫无重叠,倒排索引匹配不上,可人和AI都知道这俩说的是一件事。
二是语义模糊。用户搜一句口语化的问题,词和词之间的真实意图,字面上表达不出来。
向量检索的思路换了个方向:不匹配词,匹配意思。把词义压进一个向量里,意思近的,向量距离就小。
二、完整链路:四步走
把一件内容变成"可被语义检索"的东西,要走四步。
内容 ──①Embedding──▶ 向量 ──②相似度度量──▶ 距离/夹角
──③索引(ANN)──▶ 快速候选集
查询 ──④查询向量化──▶ 同空间向量 ──▶ 检索Top-K
第一步,Embedding(向量嵌入)。 用模型把文本、图片、音频编码成一个固定维度的稠密向量,比如768维、1024维。文字怎么变数字?早期有Word2Vec、GloVe这类词向量,现在主流是BERT、bge这类句子级模型。同一个模型产出的向量,才会落在同一个空间里,能互相比较。
第二步,相似度度量。 有了向量,得有个标准衡量"谁离谁近"。这一步看似简单,选错了后面全歪。
第三步,建索引。 百万级向量不可能逐个比,得建ANN索引把搜索范围缩小。
第四步,查询向量化。 用户查询必须用同一个模型转成向量,才能和库里向量比。这里最容易出的错,是库用A模型、查询用B模型,两个空间的向量比出来毫无意义。

图1 把内容变成"可被语义检索",走这四步
三、相似度度量怎么选
四种常用度量,各有各的脾气。
| 度量方式 | 计算的是什么 | 取值范围 | 典型场景 |
|---|---|---|---|
| 余弦相似度 | 两个向量的夹角余弦 | -1到1 | 文本语义检索,只看方向不看长度 |
| 欧氏距离 | 空间中两点的直线距离 | 0到正无穷 | 图像检索、推荐系统 |
| 内积 | 两个向量的点积 | 负无穷到正无穷 | 向量已归一化时,等价余弦 |
| 海明距离 | 两个二值向量不同的位数 | 0到向量维度 | 大规模二值向量检索 |
选择逻辑其实不复杂。文本检索一般用余弦,因为它只看向量方向,不受文本长度影响。图像、推荐常用欧氏距离。归一化之后用内积还能省一次计算,这也是很多工程实践在第一步Embedding时就顺手normalize的原因。
归一化决定了内积和余弦是否等价。做了归一化,两者可以互换;不做归一化,就得老老实实选余弦。前提是写入和查询保持一致。
度量方式定了,向量之间就能比出谁近谁远。但库里动辄几十上百万条向量,一条条算距离,查询等不起。下一步,就得靠索引把搜索范围缩下来。
四、ANN索引:用一点召回率换大幅提速
先说清为什么非建索引不可。精确检索(KNN)每次都拿查询向量和全量向量算一遍距离,结果100%准,但数据一大就慢。ANN(近似最近邻)换个思路:用一个结构,把"明显不可能最近的"先排除掉,只在候选集里精算。代价是可能漏掉极个别真正最近的,换来的是数量级的提速。
主流ANN索引,思路可以分几类:
| 索引类型 | 代表算法 | 核心思路 | 适用边界 |
|---|---|---|---|
| 基于图 | HNSW、NSG | 用邻居关系建图,从入口点逐层逼近 | 召回与速度均衡,生产最常用 |
| 基于聚类倒排 | IVF、IVF-PQ | 先聚类分桶,查询只扫最近几个桶 | 大规模、可用PQ压缩 |
| 基于量化 | PQ、二值化 | 把高维向量压成短码,快速估算距离 | 内存受限场景 |
| 基于哈希 | LSH | 让相似向量更易落入同一桶 | 特定度量下有优势,通用性弱 |
| 磁盘图索引 | DiskANN | 图索引落到磁盘,突破内存限制 | 超大规模、内存吃紧 |

图2 主流 ANN 索引按思路分五类
生产环境里,HNSW是最常见的选择,因为它在召回率和延迟之间的平衡做得不错,参数也相对好调。它的三个关键参数,这里只说结论:M和ef_construction管索引质量,ef_search管查询时的召回与延迟,调参就是在这几个旋钮上找平衡点。
一段最小的建索引和查询长这样:
-- 建HNSW向量索引
CREATE INDEX idx_doc_vec ON doc_embedding
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 200);
-- 检索:向量距离 + 标量过滤在同一条SQL里
SELECT id, title,
1 - (embedding <=> :q) AS sim
FROM doc_embedding
WHERE category = 'faq'
ORDER BY embedding <=> :q
LIMIT 10;
这段写法里藏着一个关键判断:向量检索和标量过滤能不能在同一次查询里完成。如果你的场景里,向量和业务数据强关联,要一起过滤和排序,那向量库和业务库一旦分开,就只能先召回、再过滤、再拼装,过滤条件一多,结果就容易凑不满。这也是为什么越来越多的关系型数据库把向量能力做进内核。金仓KES Vector做的事,就是把链路里的第三步(ANN索引)和第四步(查询向量化与检索)直接嵌进数据库内核,让你不用在向量库和业务库之间来回倒数据。
五、召回、延迟、更新:三个方向拉不满
很多人以为ANN参数调得越高越好。向量检索本质上是一次权衡,而且很难两头都占。
想保召回率,就把ef_search调大、用图索引,适合答案漏了代价高的场景,比如合规检索。想保延迟,就把候选池调小、用更简单的索引,服务高并发在线问答。数据更新频繁的时候,还要优先选重建代价低的索引,别死磕对写入不友好的结构。像KES Vector的向量索引,建好之后依然支持新向量的插入和删除,不用重建,对写入频繁的场景就比较友好。这三个方向基本没法同时拉满。
专业库在纯向量高并发下的极限性能更好;把向量融进关系库内核的路线(比如KES Vector),在"向量和业务数据要一起用"的场景里链路更短,一个库管住关系、文档、向量、GIS、时序,省掉跨库同步,也少维护一套系统。选哪个,取决于你的数据之间要不要互相看。
六、它最适合哪几类场景
向量检索不是万能的,但在这几类场景里几乎是标配:
企业知识库问答(把文档切片存向量,按语义召回)、语义搜索(理解意图,不卡关键词)、以图搜图(图片转向量做相似匹配)、推荐召回(用户和内容落在同一空间算相似度)、RAG(先向量检索找相关片段,再交给大模型生成答案,缓解幻觉和知识过时)。
共同点都是:数据本身是文本或多媒体,语义相似比字面匹配更重要。
七、什么时候会失效,什么时候用不上
知道边界,比知道能力更重要。
需要精确匹配的地方别用向量。 查订单号、身份证、金额区间,这些要的是"精确等于",向量检索给的是"近似相似",用错地方反而误事。这类还是走标量索引和条件查询。好在这种精确查询和语义检索不必分家:把向量做进内核的关系库(比如KES Vector),一个走标量索引、一个走向量索引,同一份数据、同一个事务,不用为加一层语义检索再单独引一套系统。
数据太少,用不上它。 几百条数据,暴力比对就够了,上ANN索引纯属给自己加复杂度。
Embedding模型选错,后面全白费。 模型和你的语种、领域不匹配,向量本身就表达不准,索引再精细也捞不回来。选型时先拿真实数据验证模型的语义表达能力。
查询和库用了不同模型,比较无意义。 这条前面提过,但踩的人最多,向量维度对上了、空间对不上,结果就是乱的。
八、避坑清单
度量怎么选、索引怎么分、维度怎么定,这些边界上面都说得差不多了。再补三条我踩过、也见别人踩过的坑,看着都不大,翻车的时候却挺让人头疼。
第一,归一化这件事,要么全程做,要么全程不做。我见过的坑里,至少有一半来自写入时归一化了、查询时忘了,余弦和内积混着用,结果忽好忽坏。
第二,别拿单次检索的"感觉"当评测。固定一批测试集,算召回率,才看得见参数调整到底有没有用。
第三,这条是我自己交的学费。我一开始以为"向量维度越高、表达越准",给一个短文本场景上了1024维模型,结果存储和检索成本上去了,效果和768维几乎没差别。维度不是越高越好,得和你的内容长度、颗粒度匹配。一般短文本768维够用,长文档或专业领域可以到1024维,再往上收益递减就很明显了。
这三条说到底是同一件事:参数和选型都得跟你的真实数据对齐,脱离数据谈好坏,没什么意义。
九、小结
向量检索的链路就四步:Embedding把意思变向量、相似度度量定规矩、ANN索引提速度、查询向量化保证同空间。理解这四步,参数和选型就不会迷路。
从工程落地上看,把向量能力放进关系型数据库内核(例如KES Vector),向量和业务数据在一个事务、一条SQL里处理,链路短、少维护一套系统。对已经在跑关系库、想低成本加语义检索能力的团队,这条路值得放进评估清单。
你被哪一步坑过,是Embedding选型、度量方式,还是索引参数?评论区说说,我挑几个典型的在下篇拆。
我是数据库小学妹,帮你少走弯路少踩坑,咱们下篇见👋
- 点赞
- 收藏
- 关注作者
评论(0)