主题
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 对比
| 维度 | IVFFlat | HNSW |
|---|---|---|
| 搜索复杂度 | O(√N) | O(log N) |
| 构建速度 | 快 | 慢 |
| 查询速度 | 快(probes 适中时) | 更快 |
| 召回率稳定性 | 依赖 probes 调优 | 更稳定 |
| 内存占用 | 小 | 大(约 2~3x 向量数据) |
| 增量更新 | 需重建索引 | 支持 |
| 空表建索引 | 不推荐 | 可以 |
| 参数调优 | lists + probes | m + 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 诊断方法。