· liyu · algorithm · 21 min read
KNN vs ANN:搜广推与向量检索中的算法原理、性能权衡与工程选型
从暴力 KNN 的 O(N × D) 算力瓶颈,到 IVF、HNSW、IVF-PQ 的亚线性近似检索原理;从搜广推全链路端到端 SLA 预算分配,到工业级向量索引在维度、内存、动态更新与属性过滤下的全景选型决策。
在大模型 RAG 知识检索与搜广推双塔召回等现代架构中,核心任务均是在海量向量数据库中快速找到与 Query 向量“最相似的 Top-K 个实体”。
然而,当候选库从 1 万扩展至 1000 万甚至 10 亿级别,向量维度从 64 维激增至 768 / 1536 维时,全量精确计算与在线毫秒级延迟预算(SLA)之间产生了不可调和的矛盾。
这就引出了近邻搜索领域的两大范式:精确近邻搜索(KNN) 与 近似近邻搜索(ANN)。
一、 KNN(暴力精确近邻):绝对精确但算力昂贵
1. 原理与实现
KNN(K-Nearest Neighbors,K 近邻)采用最直接的**穷举遍历(Flat Brute-force)**策略:
给定查询向量 q,算法逐一计算 q 与候选集中所有 N 个向量的距离(欧氏距离、余弦相似度或内积),排序后返回最小的 K 个候选。
import numpy as np
def knn_search(query: np.ndarray, candidates: np.ndarray, k: int) -> list[int]:
"""
暴力 Flat KNN 搜索
query: 查询向量, 形状 [D]
candidates: 候选向量矩阵, 形状 [N, D]
k: 返回 Top-K 数量
"""
# 1. 计算 Query 与所有 N 个候选向量的欧氏距离: O(N × D)
distances = np.linalg.norm(candidates - query, axis=1) # [N]
# 2. 堆排序或局部排序提取 Top-K: O(N + K log K)
top_k_indices = np.argpartition(distances, k)[:k]
return top_k_indices[np.argsort(distances[top_k_indices])].tolist()2. 复杂度与算力爆炸分析
- 时间复杂度:
O(N × D)。每次查询必须进行N × D次浮点乘加运算。 - 空间复杂度:
O(N × D)。必须全量无损存储所有候选浮点向量。
┌──────────────┬──────────────┬──────────────────────────────┬───────────────────┐
│ 候选集规模 N │ 向量维度 D │ 单次查询浮点乘加次数 (FLOPs) │ 内存占用 (FP32) │
├──────────────┼──────────────┼──────────────────────────────┼───────────────────┤
│ 10,000 │ 128 │ 1.28 × 10⁶ (128 万次) │ 5.12 MB (极快) │
│ 1,000,000 │ 256 │ 2.56 × 10⁸ (2.56 亿次) │ 1.02 GB (稍显吃力)│
│ 10,000,000 │ 768 │ 7.68 × 10⁹ (76.8 亿次) │ 30.72 GB (严重超时)│
│ 1,000,000,000│ 128 │ 1.28 × 10¹¹ (1280 亿次) │ 512 GB (无法单机)│
└──────────────┴──────────────┴──────────────────────────────┴───────────────────┘3. 核心优势与工程瓶颈
- 优势:召回率严格 100%(Zero Recall Loss),没有任何近似误差,实现极其直观,无需额外构建复杂的索引结构。
- 工业瓶颈:在千万级以上候选集下,单次查询耗时动辄数百毫秒乃至秒级,在高 QPS、低延迟要求的在线召回环节完全不可行。
二、 ANN(近似近邻检索):用微小精度换取极致吞吐
1. 核心设计哲学
ANN(Approximate Nearest Neighbors)的核心思想是:放弃 100% 绝对精确的保证,以可控的微小召回率损失(如保持 95%~99% Recall@K),换取亚线性(Sub-linear)乃至对数级(Logarithmic)的检索速度与存储压缩。
主流 ANN 算法通过三种空间剪枝与量化手段打破 O(N × D) 瓶颈:
- 空间划分(倒排索引):如 IVF;
- 图跳跃路由(小世界网络):如 HNSW;
- 向量压缩编码(乘积量化):如 PQ、IVF-PQ。
2. 主流 ANN 算法原理与实现
(1) IVF(Inverted File Index,倒排索引)—— 空间聚类与分区剪枝
- 核心思想:利用 K-Means 算法预先将高维空间划分为
nlist个 Voronoi 聚类胞元(Centroids)。查询时先找到离 Query 最近的nprobe个聚类中心,仅在对应的倒排桶内执行向量比对。
建索引阶段 (Offline):
全量 N 个向量 ──[K-Means 聚类]──► 产出 nlist 个聚类中心
将每个向量挂载到最近中心的倒排链表 (Inverted List) 中
查询检索阶段 (Online):
Query 向量 ──► 1. 计算与 nlist 个中心的距离: O(nlist × D)
──► 2. 选出最近的 nprobe 个桶 (如 nprobe = 16)
──► 3. 遍历这 nprobe 个桶内挂载的向量进行比对: O(nprobe × (N / nlist) × D)import faiss
dim = 128
nlist = 4096 # 聚类中心数量
quantizer = faiss.IndexFlatL2(dim)
index_ivf = faiss.IndexIVFFlat(quantizer, dim, nlist)
# 离线训练聚类中心并添加数据
index_ivf.train(train_vectors)
index_ivf.add(all_vectors)
# 在线查询: 设置探测桶数
index_ivf.nprobe = 32
distances, indices = index_ivf.search(query_vector, k=100)- 复杂度分析:
- 单次搜索总复杂度为
O(nlist × D + nprobe × (N / nlist) × D)。 - 其中
N / nlist是每个桶的平均向量数,实际只需比对nprobe × (N / nlist)个向量(例如nlist=4096, nprobe=32时,仅需扫描全库约 0.78% 的数据量)。
- 单次搜索总复杂度为
(2) HNSW(Hierarchical Navigable Small World)—— 多层图跳跃路由
- 核心思想:基于概率跳表(Skip-List)和可导航小世界图(NSW)理论构建多层图。顶层图稀疏、边跨度长,负责粗粒度的大步长高速跳跃定位;底层图稠密,负责细粒度的局部精准搜索。
Layer 2 (顶层·稀疏): 节点 A ═════════════════════════► 节点 G (大跨度粗搜)
│ │
▼ ▼
Layer 1 (中层·过渡): 节点 A ═════► 节点 D ═════════════► 节点 G (中等跨度)
│ │ │
▼ ▼ ▼
Layer 0 (底层·全量): 节点 A ─► B ─► C ─► D ─► E ─► F ─► G (局部贪心精搜)import faiss
dim = 128
M = 32 # 每个节点的最大出边邻居数
ef_construction = 200 # 构图时的束搜索宽度 (Beam Width)
index_hnsw = faiss.IndexHNSWFlat(dim, M)
index_hnsw.hnsw.efConstruction = ef_construction
index_hnsw.add(all_vectors)
# 在线查询: 控制搜索宽度
index_hnsw.hnsw.efSearch = 64
distances, indices = index_hnsw.search(query_vector, k=100)- 关键参数与复杂度:
- 搜索时间复杂度:
O(log N × D),吞吐量(QPS)与 Recall@K 极高。 - 内存代价高:除了原始向量外,底层图结构为每个节点保存
M条边指针(通常单向量额外产生 30~100+ 字节内存),且不支持原生物理硬删除(通常采用标记删除/Tombstone 机制)。
- 搜索时间复杂度:
(3) PQ(Product Quantization,乘积量化)—— 空间分解与非对称距离查表
- 核心思想:将
D维向量切分成M个低维子向量(每个子向量维度d* = D / M)。在每个子空间独立运行 K-Means 生成k*个聚类中心(通常取k* = 256,即刚好 1 字节uint8编码)。每个原始高维向量被压缩为M个字节的 ID 序列。
原始向量 (128 维 FP32 = 512 字节):
[ x₁ x₂ ... x₁₆ | x₁₇ x₁₈ ... x₃₂ | ... | x₁₁₃ ... x₁₂₈ ] (共 M = 8 个子空间,各 16 维)
│ │ │
▼ ▼ ▼
匹配子空间第 42 号中心 匹配第 187 号中心 匹配第 9 号中心
│ │ │
量化压缩结果 (8 字节 uint8):
[ 42, 187, 13, 204, 76, 91, 5, 9 ]乘积量化的检索核心:非对称距离计算(ADC, Asymmetric Distance Computation)
PQ 在检索时不重构解压候选向量,而是采用极为高效的两阶段查表法:
- 阶段 1:预构建距离查找表(LUT, Look-Up Table)
- Query 向量
q不进行量化,将其切分为M个子向量qₘ。 - 分别计算每个
qₘ与对应子空间全部k*个聚类中心(Centroids)的真实距离,生成一张大小为M × k*的距离查找表。 - 计算量:
O(M × k* × (D / M)) = O(k* × D)。若k*=256, D=128,仅需 32,768 次浮点乘加,耗时仅几微秒,且与候选集总量 N 完全无关!
- Query 向量
- 阶段 2:候选集查表与距离累加
- 遍历候选库中量化后的
N个向量,对每个向量读取其M个字节 ID,直接在 LUT 中查表并将M个距离累加:Dist(q, xᵢ) ≈ Σₘ LUT[m][ Codeᵢ[m] ] - 计算量:
O(N × M)次纯整型查表与加法指令,无任何浮点乘法,极其适合 SIMD 向量化加速。
- 遍历候选库中量化后的
Query q ──► 切分为 M 个子向量 ──► 与各子空间 256 个中心计算距离 ──► 生成 LUT [M × 256] (微秒级)
│
候选向量量化 ID: [42, 187, ...] ──► 直接在 LUT 中查表加和: LUT[0][42] + LUT[1][187] + ... ──► 近似距离严谨存储开销核算(以 10 亿条 128 维 FP32 向量为例):
- 原始数据存储:
10⁹ × 128 × 4 字节 = 512 GB - PQ 量化编码矩阵(M = 8):
10⁹ × 8 字节 = 8 GB(压缩比达 64 倍) - 常驻码本(Codebook)内存:
M × k* × (D / M) × 4 字节 = 8 × 256 × 16 × 4 字节 ≈ 128 KB(在整机内存中微乎其微)
(4) 工业首选:IVF-PQ 复合索引
在工程实践中,PQ 极少在全量 N 上直接扫表,而是与倒排索引(IVF)深度结合为 IVF-PQ:
- 先用粗量化器(IVF)定位到最近的
nprobe个桶; - 再对桶内残差向量(Residual Vectors)进行 PQ 查表累加。
- 单次检索复杂度:
O(nlist × D + nprobe × (N / nlist) × M),同时获得 IVF 的空间剪枝加速与 PQ 的极致内存压缩。
3. 主流向量检索算法全方位对比
| 算法类型 | 索引构建耗时 | 搜索时间复杂度 | 内存占用 (每向量) | 召回率 (Recall@K) | 适用候选规模 |
|---|---|---|---|---|---|
| Flat (KNN) | 0 (无索引) | O(N × D) | 原始尺寸 (D × 4 字节) | 100% (绝对基准) | < 5 万 |
| IVF-Flat | 快 (K-Means) | O(nlist·D + nprobe·(N/nlist)·D) | 原始尺寸 + 少量倒排指针 | 85% ~ 95% | 10 万 ~ 500 万 |
| HNSW | 较慢 (多层连边) | O(log N × D) | 高 (D×4 + M×8 字节) | 95% ~ 99%+ | 10 万 ~ 5000 万 |
| PQ (Flat) | 中等 (各空间聚类) | O(k*·D + N·M) | 极低 (M 字节 + 128KB 码本) | 70% ~ 85% | 100 万 ~ 1 亿 |
| IVF-PQ | 中等 | O(nlist·D + nprobe·(N/nlist)·M) | 极低 (M 字节 + 倒排开销) | 80% ~ 92% | 1000 万 ~ 10 亿+ |
三、 搜广推多层漏斗与端到端延迟预算(SLA)分配
在真实的工业级搜索、推荐与在线广告系统中,系统采用典型的分层漏斗级联架构。
1. 端到端延迟 SLA 的共享预算本质
在线服务对端到端(End-to-End)P99 延迟有极严苛的 SLA 限制:
- 电商/内容推荐:端到端通常限制在 100 ~ 200ms 之间。
- 搜索系统:端到端通常限制在 150 ~ 250ms 之间。
- 在线广告竞价(RTB):由于下游 DSP 交互与媒体展示限制,端到端通常被极限压缩至 30 ~ 50ms。
各阶段的时间预算是共享的配额系统,前序环节耗时超标必然压缩后续精细排序的算力窗口。
以推荐系统端到端 150ms 典型预算分配为例:
[用户请求到达] ──► 网络与网关解析 (5~10ms)
│
▼
┌──────────────────────────────────────────────────────────┐
│ 1. 召回层 (Retrieval / Matching): 15~30ms │
│ - 候选规模: 10,000,000 ➔ 1,000 ~ 3,000 (多路并发召回) │
│ - 技术核心: ANN 向量检索 (IVF-PQ / HNSW) + 规则/倒排 │
└──────────────────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ 2. 粗排层 (Pre-ranking): 10~20ms │
│ - 候选规模: 3,000 ➔ 300 ~ 800 │
│ - 技术核心: 轻量级双塔模型 / 小参数深度网络 / 级联打分 │
└──────────────────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ 3. 特征工程与服务读取 (Feature Store Fetching): 10~20ms │
│ - 批量获取数百候选的实时交叉特征与用户实时行为画像 │
└──────────────────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ 4. 精排层 (Ranking): 40~70ms │
│ - 候选规模: 300 ~ 800 ➔ 50 ~ 100 │
│ - 技术核心: 重度多目标深度模型 (DIN/DLRM/RankGPT) │
│ - 追求极高预估准确度 (CTR / CVR / 停留时长等) │
└──────────────────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────┐
│ 5. 重排与业务机制 (Re-ranking & Policies): 10~20ms │
│ - 候选规模: 50 ~ 100 ➔ 最终呈现 10 ~ 20 │
│ - 技术核心: 多样性打散 (DPP)、控频去重、商业化广告混排 │
└──────────────────────────────────────────────────────────┘2. 各层近邻检索与模型算力的分工逻辑
(1) 召回层:ANN 是打破算力天花板的唯一解
- 召回层必须在 15~30ms 内完成千万级物料库的初筛。
- 关于“漏召回”的严谨认知:ANN 并非“完美无害”,若因近似索引遗漏了处于聚类边界的高转化爆款商品,会直接导致后序精排完全无缘处理该候选,造成真金白银的 GMV / CTR 损失。
- 工程应对策略:
- 多路召回融合:结合协同过滤、向量召回(双塔+ANN)、图召回、类目热门等互补召回源;
- 索引参数调优与 Re-ranking Refinement:加大
nprobe/efSearch,或使用IVF-PQ + FP32 Flat 重打分(先取 Top-2000 量化候选,再用原始全量向量做精确距离修正)。
(2) 精排层:重型交叉模型,而非“近邻算法”
- 精排层面对的是经过前两道漏斗过滤出的数百个候选。
- 此时任务已从“空间近邻查找”转变为复杂特征交互的多目标预估(Scoring & Ranking)。模型需要融合用户画像、上下文特征、物料多模态特征以及实时的序列注意力(如 DIN 捕获短期兴趣转移)。
- 之所以能承受高算力重型模型,正是得益于前序 ANN 召回将候选规模从千万级压缩了 4 个数量级。
四、 工业级向量检索选型决策指南
选择向量检索索引方案时,不能仅依据“向量条数”简单拍脑袋,需综合权衡以下 5 大核心工程维度:
┌──────────────────────── 向量检索工程选型决策路径 ────────────────────────┐
│ │
│ 1. 内存预算充裕 + 追求极限召回率 (Recall > 98%) + 低延迟? │
│ └──► 首选 HNSW (适合千万元级别、单机大内存/集群方案) │
│ │
│ 2. 候选库十亿级 + 内存成本敏感 + 接受适度精度折损 (Recall 80~90%)? │
│ └──► 首选 IVF-PQ 或 SCaNN (配合残差量化与重排打分) │
│ │
│ 3. 高频实时写入 + 频繁动态删除? │
│ └──► 优先选 IVF-Flat 或 专门优化动态 LSM 树图索引的向量数据库 │
│ │
│ 4. 带有强属性硬过滤条件 (如: 城市=北京 AND 价格<100)? │
│ └──► 单阶段图内迭代过滤 (Single-stage Filtered HNSW) 或 IVF 倒排合并 │
│ │
│ 5. 向量维度极高 (D ≥ 768 / 1536)? │
│ └──► 需警惕高维距离失效与图构建膨胀,通常先做 PCA 降维或采用两级量化 │
│ │
└───────────────────────────────────────────────────────────────────────────┘1. 内存成本与硬件预算(TCO)
- HNSW:单向量存储开销达
(D × 4 + M × 8)字节。1 亿 768 维向量需要约 350 GB+ 纯内存,硬件成本高昂。 - IVF-PQ:可压缩至每个向量 8~64 字节,1 亿向量仅需数 GB 内存即可常驻,单台低配机器即可承载。
2. 向量维度与高维稀释(Curse of Dimensionality)
- 随着维度
D增加(如 1536 维),欧氏距离区分度下降,HNSW 图遍历剪枝效率降低,距离计算开销成倍上涨。 - 工业界常通过 PCA / Matryoshka 嵌套表示学习(MRL) 将高维向量裁剪至 128~256 维,再构建 ANN 索引。
3. 动态更新与删除支持(Real-time Ingestion)
- HNSW:插入新节点需在各层执行束搜索连边,耗时较高;删除节点会导致图拓扑断裂,多数引擎仅做软删除(Soft Delete),定期全量重建。
- IVF:向指定倒排桶追加新向量极为轻量,但聚类中心漂移后需要离线重聚类(Re-clustering)。
4. 标量属性混合检索(Hybrid Filtered Search)
- 在电商与搜索中,几乎所有向量检索都附带标量过滤条件(如“只要在售、自营、价格区间”)。
- 后过滤(Post-filtering):先 ANN 取 Top-1000 再按标量过滤,若过滤条件极其严苛(保留率 < 1%),可能导致返回结果为空;
- 先过滤(Pre-filtering):先按标量筛选出 ID,再在子集上做检索(HNSW 图结构在此场景下易退化,IVF 更易与传统倒排求交)。
五、 3 句话总结
- KNN 与 ANN 的本质:KNN 追求 100% 无损精度但受制于
O(N × D)算力爆炸;ANN 通过空间分区(IVF)、图跳跃(HNSW)与乘积量化(PQ)实现亚线性高效检索。 - 搜广推的算力分配法则:在线延迟 SLA 是严格共享的;召回层用 ANN 解决“千万级大海捞针的可行性”,精排层用重型深度模型解决“数百级细粒度排序的准确性”。
- 工程选型心法:不仅看数据量,更要结合向量维度、内存预算(TCO)、实时更新机制与标量过滤要求,在 HNSW、IVF 与 IVF-PQ 之间取得最佳平衡。