跳转到内容

5.3 HNSW 索引

HNSW 是目前最主流的向量索引——O(log N) 的搜索速度、稳定的召回率、支持增量更新


这一节在讲什么?

HNSW(Hierarchical Navigable Small World)是 pgvector 0.5.0 之后引入的索引类型,也是目前生产环境中更推荐的选择。相比 IVFFlat,HNSW 有三个核心优势:搜索速度更快(O(log N) vs O(√N))、召回率更稳定(不依赖 probes 调优)、支持增量更新(不需要重建索引)。这一节我们要理解 HNSW 的原理、关键参数 m 和 ef_construction/ef_search 的调优方法、以及它与 IVFFlat 的详细对比。


HNSW 原理

HNSW 的核心思想是构建一个多层图结构——上层是稀疏的"高速公路"(连接少数关键节点),下层是密集的"地方道路"(连接大量节点)。搜索时从最上层的入口节点开始,逐层向下导航,每一层都沿着距离查询向量最近的邻居前进,最终在底层找到最近邻。

┌──────────────────────────────────────────────────────────────────┐
│  HNSW 多层图结构                                                 │
│                                                                  │
│  Layer 2(最稀疏):  A ─────────────────── B                     │
│                      │                          │                │
│  Layer 1:            C ────── D ────── E ────── F                │
│                      │       │       │       │                   │
│  Layer 0(最密集):  G─H─I─J─K─L─M─N─O─P─Q─R─S─T                │
│                                                                  │
│  搜索过程:                                                      │
│  1. 从 Layer 2 的入口 A 开始                                     │
│  2. A 比 B 更近查询向量 q → 选择 A                               │
│  3. 下降到 Layer 1,从 A 的邻居 C 开始                           │
│  4. C→D→E,E 是 Layer 1 中离 q 最近的                            │
│  5. 下降到 Layer 0,从 E 的邻居 K 开始                           │
│  6. K→L→M,M 是 Layer 0 中离 q 最近的 → 返回 M                  │
│                                                                  │
│  → 每层只需检查少量节点,总搜索复杂度 O(log N)                   │
└──────────────────────────────────────────────────────────────────┘

创建 HNSW 索引

sql
-- 创建 HNSW 索引(余弦距离)
CREATE INDEX idx_docs_embedding_hnsw ON documents
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);

-- 创建 HNSW 索引(L2 距离)
CREATE INDEX idx_docs_embedding_hnsw_l2 ON documents
USING hnsw (embedding vector_l2_ops)
WITH (m = 16, ef_construction = 64);

与 IVFFlat 不同,HNSW 可以在空表上创建索引——它不需要预先知道数据分布。新数据插入时会自动更新图结构。


关键参数调优

m:每层最大连接数

m 控制图中每个节点最多有多少条边(连接)。m 越大,图的连通性越好,搜索时能探索更多路径,召回率更高——但索引占用的内存也更大。

m 值召回率索引大小建索引速度推荐场景
8较低内存受限
16(默认)良好中等中等大多数场景
32追求高召回率
64很高很大很慢极致召回率

推荐:大多数场景用默认的 m=16 即可。如果召回率不够,先尝试增加 ef_search 而不是 m。

ef_construction:建索引时的搜索宽度

ef_construction 控制建索引时搜索最近邻的宽度。值越大,建索引时找到的邻居越精确,索引质量越高——但建索引的时间也越长。

ef_construction索引质量建索引时间推荐
32较低不推荐
64(默认)良好中等大多数场景
128数据量 > 1M
200+很高很慢极致质量

推荐:数据量 < 100K 用 64,数据量 > 1M 用 128~200。

ef_search:查询时的搜索宽度

ef_search 控制查询时搜索的宽度。值越大,搜索越精确,但延迟也越高。它通过 SET 命令设置:

sql
-- 设置 ef_search(默认 40)
SET hnsw.ef_search = 100;

-- 执行查询
SELECT id, content, embedding <=> '[0.1, ...]' AS distance
FROM documents
ORDER BY embedding <=> '[0.1, ...]'
LIMIT 5;
ef_search召回率查询延迟推荐
10最快不推荐
40(默认)良好大多数场景
100中等追求高召回率
200+很高较慢极致召回率

推荐:从默认的 40 开始,如果召回率不够逐步增加到 100~200。


HNSW vs IVFFlat 对比

维度IVFFlatHNSW
搜索复杂度O(√N)O(log N)
构建速度
查询速度快(probes 适中时)更快
召回率稳定性依赖 probes 调优更稳定
内存占用大(约 2~3x 向量数据)
增量更新需重建索引支持
空表建索引不推荐可以
参数调优lists + probesm + ef_construction + ef_search
推荐场景数据量大且稳定数据量中等且频繁更新

实践建议:除非你有特殊原因(如内存受限),否则优先选择 HNSW。


常见误区

误区 1:HNSW 索引占用的内存和 IVFFlat 一样

HNSW 的内存占用通常是 IVFFlat 的 2~3 倍,因为它需要存储图的边信息。对于 100 万条 384 维向量,HNSW 索引约需 2~3 GB 内存,而 IVFFlat 只需约 1 GB。

误区 2:ef_search 设得越高越好

ef_search 过高会增加查询延迟。对于大多数 RAG 场景,ef_search=40~100 已经能获得 95%+ 的召回率,不需要设到 200+。

误区 3:HNSW 索引不需要维护

虽然 HNSW 支持增量更新,但大量删除后图的连通性可能下降。建议在大量删除后重建索引:

sql
REINDEX INDEX idx_docs_embedding_hnsw;

本章小结

HNSW 是 pgvector 中更先进、更推荐的向量索引。核心要点回顾:第一,HNSW 通过多层图结构实现 O(log N) 搜索,比 IVFFlat 的 O(√N) 更快;第二,m=16、ef_construction=64、ef_search=40 是大多数场景的推荐起点;第三,HNSW 支持空表建索引和增量更新,比 IVFFlat 更灵活;第四,HNSW 的内存占用是 IVFFlat 的 2~3 倍,内存受限时考虑 IVFFlat;第五,除非有特殊原因,优先选择 HNSW。

下一节我们将讲索引选型的完整决策树和 EXPLAIN ANALYZE 诊断方法。

基于 MIT 许可发布