· liyu · algorithm · 21 min read

KNN vs ANN:搜广推与向量检索中的算法原理、性能权衡与工程选型

从暴力 KNN 的 O(N × D) 算力瓶颈,到 IVF、HNSW、IVF-PQ 的亚线性近似检索原理;从搜广推全链路端到端 SLA 预算分配,到工业级向量索引在维度、内存、动态更新与属性过滤下的全景选型决策。

从暴力 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) 瓶颈:

  1. 空间划分(倒排索引):如 IVF;
  2. 图跳跃路由(小世界网络):如 HNSW;
  3. 向量压缩编码(乘积量化):如 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. 阶段 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 完全无关
  2. 阶段 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)。
  • 在电商与搜索中,几乎所有向量检索都附带标量过滤条件(如“只要在售、自营、价格区间”)。
  • 后过滤(Post-filtering):先 ANN 取 Top-1000 再按标量过滤,若过滤条件极其严苛(保留率 < 1%),可能导致返回结果为空;
  • 先过滤(Pre-filtering):先按标量筛选出 ID,再在子集上做检索(HNSW 图结构在此场景下易退化,IVF 更易与传统倒排求交)。

五、 3 句话总结

  1. KNN 与 ANN 的本质:KNN 追求 100% 无损精度但受制于 O(N × D) 算力爆炸;ANN 通过空间分区(IVF)、图跳跃(HNSW)与乘积量化(PQ)实现亚线性高效检索。
  2. 搜广推的算力分配法则:在线延迟 SLA 是严格共享的;召回层用 ANN 解决“千万级大海捞针的可行性”,精排层用重型深度模型解决“数百级细粒度排序的准确性”。
  3. 工程选型心法:不仅看数据量,更要结合向量维度、内存预算(TCO)、实时更新机制与标量过滤要求,在 HNSW、IVF 与 IVF-PQ 之间取得最佳平衡。
Share:
Back to Blog

Related Posts

View All Posts »
当双塔模型遇见 RAG:召回与检索,殊途同归

当双塔模型遇见 RAG:召回与检索,殊途同归

搜广推系统中的双塔模型和 RAG 中的向量检索,一个诞生于推荐系统,一个兴起于大模型时代。当我把两者的架构图放在一起时,突然发现它们竟如此相似——本质上都是在向量空间中寻找最近的"灵魂伴侣"。

搜索算法基石:从 TF-IDF 到 BM25 的演进之路

搜索算法基石:从 TF-IDF 到 BM25 的演进之路

在搜广推系统中,搜索是最核心的能力之一。本文从信息检索的经典算法 TF-IDF 出发,深入剖析其原理与局限,再引出工业界广泛使用的 BM25 算法,探讨它如何优雅地解决 TF-IDF 的不足。