跳到正文
AI 知识地图 0.18 · 2026-07-30
关于与纠错文字目录 / Search
理解原理

向量数据库:在大规模嵌入中做近似近邻检索

理解向量、距离度量、HNSW/IVF/PQ、过滤、更新与一致性,并把索引 Recall、延迟和端到端 RAG 分开。

核心命题 向量数据库把对象嵌入为向量并用近似近邻索引加速相似搜索;它管理的不只是向量,还包括标识、元数据、权限、版本和原文映射,近似索引需用召回—延迟—内存权衡。
读完你应该能:区分精确与 ANN;解释 HNSW/IVF/PQ;设计元数据过滤;管理嵌入版本和删除。
  1. 文档切分并生成版本化嵌入
  2. 写入向量与原文元数据
  3. ANN 找相似候选
  4. 过滤权限和时效
  5. 取回原文并重排
  6. 以索引和最终答案双层评测

1向量只是一种检索键直觉

向量数据库的输入从来不只是裸向量。每条入库记录都应当携带对象嵌入、稳定的文档或分块 ID、原文位置、元数据、权限信息和版本号;数据库的输出则是相似候选以及可以取回的原始内容。这一进一出的不对称,决定了向量在整个检索链路中的真实身份:它只是搜索键,而不是答案本身。

存储阶段要回答的第一个问题是:向量存进去之后,原文和权限去哪了?嵌入模型把一段文本压缩成固定维度的数值序列,压缩过程必然丢弃原文信息,向量本身无法还原出原始语句。如果库里只保存向量,命中之后就没有任何可展示、可校验的内容。因此每个向量必须关联一个稳定的文档或分块 ID、一份元数据和一个明确的原文位置。ID 让同一段内容在后续更新、删除时能被精确定位;原文位置让检索结果能够回取原文;元数据则承载语言、来源、时间等业务属性,供过滤和权限判断使用。

检索阶段的因果链因此分为两步。第一步,向量只负责找候选:查询向量与库中向量比较,得到一组在表示空间里接近的对象。第二步,答案必须回到原文并做权限校验:按候选的 ID 取回原文位置处的内容,再以当前请求主体的身份检查权限。高相似度只表示两个对象在嵌入空间里距离近,并不表示内容在事实上可信,也不表示当前主体有权查看。相似度是召回依据,原文与权限才是回答依据,二者不能混为一谈。缺少原文映射或权限字段的向量,即使相似度计算完全正确,也不能直接服务于回答。

容量规划同样要围绕真实数据规模展开。规划时应使用真实向量数、维度、数据类型、索引边数、副本数和增长率,而不只是原始文件大小。原始文件大小反映的是文本体量;向量索引占用的内存与存储则由向量条数乘以单条向量字节数、索引图连接的边数以及副本份数共同决定,并且随数据增长而增长,因此容量必须按这些参数估计。

上线前的验证也要以“业务证据是否仍能找到”为验收标准。预先保存一组精确近邻基准查询,并按租户过滤率、语言、文档新旧和难度切片。此后每次调整 ef、probes、量化或分片等参数,都要在同一基准上同时复测 Recall、p95/p99 延迟、内存占用和空结果率。只有基准不变并同时观察这四项,才能区分“索引变快了”和“业务证据仍可找到”这两种结果:前者只说明速度改善,后者才说明召回质量没有被调参破坏,业务上仍能找到该找到的内容。

2距离与归一化数学

向量检索的核心操作是把查询向量与库中候选向量逐一比较,得到相似度分数并据此排序。分数如何计算,取决于所选的度量。常用的三种度量是余弦相似度、点积和欧氏距离,它们之间的关系可以用向量的模长与夹角写清楚。

设两个向量为 a 与 b,模长分别为 ‖a‖ 和 ‖b‖,夹角为 θ。点积定义为 a·b = ‖a‖·‖b‖·cos θ;余弦相似度是把点积除以两个模长,即 cos θ = a·b / (‖a‖·‖b‖),它只度量方向的一致性,与长度无关;平方欧氏距离为 ‖a − b‖² = ‖a‖² + ‖b‖² − 2·a·b,它度量两个端点在空间中的实际远近。

三种度量何时给出相同的排序?关键在于归一化。若所有向量都做了 L2 归一化,即缩放成单位长度 ‖a‖ = ‖b‖ = 1,则点积退化为 a·b = cos θ,与余弦相似度完全一致;平方欧氏距离也退化为 ‖a − b‖² = 2 − 2·cos θ,距离越小 ⇔ 余弦越大。于是最大点积、最大余弦与最小平方欧氏距离三种排序完全等价,任选其一实现结果相同。平方欧氏距离与欧氏距离的排序也相同,因为平方是单调递增函数,工程实现里常比较平方距离以省去开方运算。

未归一化时三者不再等价。向量模长会混入点积:在同一个夹角下,模长更大的向量点积更大,点积排序被长度偏置;余弦相似度因除以了模长而不受长度影响;欧氏距离则同时受方向和长度影响。因此距离度量必须与嵌入模型的训练方式保持一致:若模型训练时以余弦或归一化点积为目标,推理时也应使用归一化向量和对应的度量;若训练时未归一化,点积目标本身就包含模长的作用,推理时也应保持未归一化,否则度量的含义与训练目标不再对应。

无论选用哪种度量,相似度分数都只是排序依据,而不是“答案正确”的概率。分数高只说明候选在表示空间里与查询接近,不能据此断定内容在事实上可信。

3为什么使用近似索引ANN

精确的近邻搜索其实很简单:对每个查询,把它与库中全部 N 个向量逐一计算距离,排序后取前 K 个。问题在于成本随库大小线性增长。每处理一个查询都要完成 N 次距离计算,当库达到百万、千万级时,单次查询的延迟和每秒的内存带宽消耗都会随之线性放大,无法满足在线服务要求。这就是全库逐个比较不可扩展的原因。

近似索引的思路是:不逐一比较所有向量,而是预先组织或压缩数据,让每次查询只访问其中一小部分,从而把延迟和内存降下来,代价是可能漏掉真正的最近邻。ANN 索引的输入是大量向量和一个查询,输出是近似的 top-k 邻居。HNSW、IVF、PQ 三种典型结构代表了三种不同的“少访问或少存储”策略。

HNSW 构建一个多层邻接图:每个向量是图中的一个节点,边连接彼此接近的节点;上层稀疏、边跨度大,负责快速跳到大致区域,下层稠密、边跨度小,负责精细定位。查询从顶层入口出发,贪心地移向与查询更近的相邻节点,再逐层下降到最底层。整个过程只访问图中极小一部分节点,时间成本远低于全量扫描;但贪心导航可能在某一层走进次优路径,从而错过真正的最近邻,这就是召回损失的来源。

IVF 走的是“先选桶再细搜”的路线。它先把全库向量划分成若干粗聚类桶,每个桶有一个代表点;查询时先计算查询向量与各桶代表点的距离,只挑最近的少数几个桶,再在桶内做更精确的比较。被访问的向量数因此大幅减少。风险在于真近邻可能落在未被选中的桶里,一旦桶选错,该近邻就找不回来。

PQ 则压缩向量本身。它把每个向量近似成一段短码,单条向量的存储开销大幅下降,于是同样内存能容纳更多向量,距离也在短码上近似计算,减少存储与带宽占用。短码是对原向量的有损近似,量化误差可能改变近邻的相对次序,同样带来召回损失。

三种策略的共同逻辑是:用少量的召回损失换取延迟和内存上的数量级改善,而这笔交换是否划算,取决于场景。当数据量很小、全量比较本就足够快,或者业务必须保证找到精确近邻时,就应该使用全量精确搜索;不能因为近似索引听起来“更先进”就强行使用。

4索引参数的三角权衡

近似索引的每个参数都在召回质量、延迟和内存三者之间做交换,调参的本质是在这个三角里选出一个可接受的工作点。以 HNSW 的 ef 和 IVF 的 probes 为例,它们控制的是查询时的搜索广度。

HNSW 在图上导航时维护一个候选集合,ef 决定这个集合的规模,也就是搜索过程中最多考察多少个候选节点。ef 越大,访问的节点越多,找到真近邻的机会越大,Recall@k 随之上升;但每多访问一个节点就要多做距离计算,延迟同步上升。IVF 的 probes 控制查询时细搜的桶数:probes 越大,被细搜的桶越多,真近邻被覆盖的概率越高,Recall 上升;同时需要比较的向量数增多,延迟增加。两者遵循同一条规律:搜索更广通常提升召回,也增加延迟。

另一些参数作用在建库侧。建图时每个节点连多少条边,直接决定索引的内存占用和构建成本:边越多,图越密,内存和构建时间越大。量化精度决定每个向量压缩后的短码长度:短码保留的信息越多,召回越高,单条向量占用的内存也越多。因此图连边数与量化精度主要影响内存与构建,而 ef 与 probes 主要影响查询延迟,四者共同落在 Recall—latency—memory 的三角里。

正确的调参必须有一把外部尺子:先用目标查询集算出精确近邻作为基准,再把近似索引在同一查询集上的结果与精确结果对照,得到 Recall;同时记录延迟、内存和构建成本。每次改变参数后重复这一过程,就能画出 Recall—latency—memory 曲线。参数一旦变化,就必须和精确邻居重新对照,因为任何“更快”或“更省”的调整都可能悄悄牺牲召回。

曲线上的每个点都是一组权衡,选点的标准是业务证据召回与 SLO,而不是把单一 QPS 最大化。应当选择召回满足业务底线、同时延迟与内存落在服务等级目标内的配置,而不是追求吞吐量指标最高的那一个。

5过滤与权限安全

向量检索的排序只知道“像不像”,不知道“能不能看”。当查询带上租户和权限条件时,就会出现一个现实问题:先向量搜索再过滤,为什么可能一个结果都没有?

第一种做法是后过滤:先对全库做向量搜索得到全局 top-k,再把当前主体无权访问的结果删掉。失效的原因在于索引不知道权限。全局 top-k 可能几乎全部由其他租户或无权限文档的向量占据——它们在表示空间里与查询同样接近,却不属于当前主体的允许域。删光之后剩下的结果可能不足 k 个,甚至一个都没有,尽管允许域内明明存在相似内容,只是排在全局第 k 名之后。向量排序与权限无关,后过滤因此会把无权结果删光。

第二种做法是前过滤:先圈出允许域再做向量搜索。它的代价落在索引效率上。近似索引是针对整个向量集合建立的结构,图的邻接关系、聚类的桶划分都是全局形成的,把搜索突然限制在一个分散的子集上,导航和桶选择的前提被破坏,索引的加速假设不再成立,延迟可能大幅上升。

因此工程上需要过滤感知搜索:把过滤条件推进搜索过程内部,让遍历只在允许的候选域内展开,或者先取多于 k 的候选、在其中应用过滤并继续补充。它的目标只是候选覆盖——保证允许域内的相似向量真的进入候选集。这解决了结果数量不足的问题,却不等于完成了授权。

真正的鉴权必须在返回之前由权威系统执行,而且针对的是原文而不是相似度分数。无论索引元数据声称什么,任何敏感内容都不能只依赖索引中的权限字段:向量索引不是授权系统,模型和索引都不具备鉴权能力。检索阶段按允许域给出候选,返回原文前再由权威鉴权做最终裁决,两个环节缺一不可。

6更新、删除与版本运维

向量库不是静态的:文档会更新、删除,嵌入模型也会升级换版。这两类变化都牵涉“新旧数据能否共存”的问题。

先看换模型。不同嵌入模型、甚至同一模型的不同版本,输出的是不同的几何空间:坐标含义、维度尺度、语义分布各不相同,旧向量与新向量之间的距离数值没有可比性。因此换模型后旧向量不能与新向量混用——混在一起查询,排序分数跨空间比较,结果没有意义。解决方式是让索引记录每个向量的 embedding 版本,迁移时采用双写重建加原子切换:在新命名空间里用新模型重建索引,同时旧命名空间继续对外服务;重建完成后,一次原子操作把流量切到新命名空间。原子切换保证了任何时刻查询要么全用旧空间、要么全用新空间,不会出现混比分数的中间状态。

再看删除。删除一个文档不只是删掉索引里的向量。答案服务要回到原文,缓存和副本也在各自位置上保存着内容,因此删除必须覆盖向量、元数据、原文、缓存和副本整条链路。只删向量不删原文,原文仍可能通过其他路径被取回;只删主库不删副本,副本上仍保留内容,后续查询仍可能命中已删除的数据。同时需要监控两种异常:陈旧记录——已删除但仍在某处可见;孤儿记录——原文已不存在、向量却还留在索引里。

生命周期的关键约束是传播未完成时的行为:当删除或更新还没有贯穿整条链路时,结果必须标记为陈旧,或者直接停止服务,绝不能继续返回可能已失效的内容。此外,贯穿整个更新流程的一个提醒是:高向量相似度不等于事实相关或可信,更新与删除操作的依据是业务事实与权限,而不是相似度分数。

7百万向量为什么需要索引运行示例

把规模具体化,才能看清“为什么需要索引”以及“近似”到底牺牲了什么。假设库里有一百万个 768 维的 FP32 向量,每个维度占 4 字节。原始向量内存为 1,000,000 × 768 × 4 字节,约 3.07 GB(2.86 GiB)——而这只算了载荷本身,图边、id、元数据和内存分配器开销还没计入。

精确扫描的代价落在查询侧:每个查询都要对全部 768M 个维度做乘加,还要把 3.07 GB 数据过一遍内存。库越大,单查询延迟和带宽消耗线性放大,全库逐个比较在百万级规模下因此不可行。图 1 中的 HNSW 用分层邻接图减少访问的节点数,把每个查询的访问量从百万级降到一小部分;“近似”因此是显式的召回—延迟取舍,而不是免费的加速。衡量取舍的尺子必须以精确搜索作为离线基准:先把精确 top-k 算出来,再统计近似结果命中了其中几条。例如近似 top-k 命中精确 top-k 中的 9 条,Recall@10 = 90%。

90% 这个数字的含义必须限定清楚。它只说明近似索引复现了精确向量排序的 90%,除此之外什么也没说。证据相关性与答案质量不在这个指标之内:如果嵌入模型本身没有把正确证据排进精确 top-k,索引 Recall 再高也无法修复语义表示的缺陷。近似索引的上限就是精确排序,它复现排序,不产生新的语义。

“只存 3 GB”的估算仍然是低估。HNSW 每个点的邻接边、元数据索引、副本和服务开销会继续放大实际占用。想压缩载荷可以用 8 bit 量化,单条向量降到 768 × 1 字节,原始载荷约 0.77 GB,但量化码本的存储和精度损失并未计入,且量化改变的是距离的近似方式,会进一步影响召回。因此容量与性能的结论必须在真实查询集上共同画出 Recall、p95 延迟、内存和构建时间曲线,而不是只比较数据库宣传的 QPS。

精确扫描:查询与全部 1,000,000 个点比较768M 维度乘加 / query结果精确 · 成本随 N 线性增长HNSW:从稀疏层跳到局部密集层L2 稀疏层L1 中间层L0 密集层只扩展有希望的邻居;更快,但可能错过真正最近点ANN 验收:把近似 top-k 与精确 top-k 对比Recall@10 = |ANN₁₀ ∩ Exact₁₀| / 10例如命中 9 个:Recall@10 = 90%
图 1 HNSW 用分层邻接图减少访问节点数;“近似”是显式的召回—延迟取舍,必须用精确搜索作为离线基准。
项目计算结果还未计入
原始向量内存1,000,000×768×4 byte约 3.07 GB(2.86 GiB)图边、id、元数据、allocator
一次精确点积1,000,000×768768M 维度乘加取 top-k、内存搬运
ANN top-10近似集命中精确集 9 条Recall@10=90%证据相关性与答案质量
向量压缩 8 bit768×1 byte/向量原始载荷约 0.77 GB量化码本和精度损失

8过滤、版本与删除怎样穿过全链路一致性边界

当一名用户只能访问全库 1% 的文档时,“先取全局 top-k 再过滤”几乎注定失败,原因可以用期望值直接算出来。假设全库 100 万块中,租户 A 只有 1 万块,占比 1%。若相似候选与租户近似独立,那么全局 top-k 里属于 A 的数量期望仅为 10 × 1% = 0.1 条——后过滤很可能返回空集。把 top-k 扩大到 100,期望也只有 1 条,而扫描范围、网络开销和越权泄露风险同时上升。注意这是一个独立假设下的容量直觉,用于判断“这样做通常不行”,不是对单次查询的保证。正确的方案是让索引搜索本身感知租户与权限过滤,只允许在域内产生候选,并在返回前再由权威鉴权层复核。

过滤只是整条链路里的一个环节。更新、撤权、删除、升级四类生命周期事件,各自要求一组对象同步、一个可观测不变量,以及对应的失败表现:

删除的安全落地通常是异步流程,但异步不等于无约束:必须有完成状态、重试、审计和最大传播时间。嵌入升级则采用蓝绿重建:用新编码器把新向量写入独立命名空间,离线回归验证后,原子地切换查询别名。不要在同一索引里逐条用新向量覆盖旧向量——过渡期内查询向量无法同时解释两个几何空间,混写必然产生无意义的分数。

失败边界要反复强调:元数据过滤是缩小候选的检索条件,不是最终授权。索引配置错误、缓存遗漏或复制延迟都可能让过滤被绕过;敏感原文必须在取回/返回边界上再次按当前主体、资源和动作做鉴权,而不是信任检索阶段给出的“已过滤”结论。

生命周期事件必须同步的对象可观测不变量失败表现
文档更新原文、块、向量、版本、缓存活动版本只有一套可检索映射新旧条款同时命中
权限撤销元数据过滤与权威 ACL撤销后查询无法取回原文向量命中后越权回填
文档删除主索引、副本、缓存、备份策略无孤儿向量与可访问缓存“幽灵”片段继续被引用
嵌入升级独立命名空间与查询编码器同一请求只比较同版本空间分数无意义、召回突降

9把因果链连起来综合

一条查询从入库到回答,经过的每个环节都以前一个环节的产物为输入,任何一环断裂都会让最终答案失去依据。

起点是文档切分与嵌入。原始文档被切成块,用带有版本标记的嵌入模型生成向量;版本被记录下来,因为不同版本的向量属于不同几何空间,之后检索时只能在同一空间内比较。

第二步是写入。向量连同原文、元数据一起入库:每个向量都关联稳定的 id、原文位置、权限和时效信息。向量只是搜索键,答案最终要回到原文,因此这一层缺失任何字段,后面的环节都无法完成。

第三步是候选生成。查询向量进入 ANN 索引,得到近似的相似候选。索引用少量召回损失换取了延迟与内存的数量级下降,其代价必须通过与精确近邻对照的 Recall 来衡量。

第四步是过滤。候选按权限与时效被缩小到当前主体允许、且仍然有效的范围。这一步必须是过滤感知的——先取全局 top-k 再过滤,在低过滤率租户下期望命中数可能远小于 1,结果为空;同时过滤只是缩小候选的检索条件,不是授权本身。

第五步是取回与重排。系统按候选 id 取回原文,必要时做重排,并在返回边界上以当前主体的权限做最终鉴权。此时返回的才是可供回答使用的原始内容,而不是向量分数。

最后是双层评测。第一层看索引:在目标查询集上对照精确近邻,共同测量 Recall、延迟、内存与构建成本,确认近似索引确实复现了精确排序。第二层看最终答案:确认正确证据真的进入了答案——因为索引 Recall 再高,也只能复现嵌入模型给出的排序;如果嵌入没有把正确证据排进精确 top-k,索引无法修复语义表示的缺陷。两层评测一起,才把“索引更快”和“业务证据仍能找到”区分开。

资料来源与改编说明
访问日期:2026-07-22