如何构建和使用向量索引?HNSW 和 IVF 有什么区别?
如何构建和使用向量索引?HNSW 和 IVF 有什么区别?
向量索引是什么?
在RAG系统中,向量数据库里存储的文档块(Chunks)可能有成百上千万个。向量索引就是一种特殊的数据结构 ,它的作用是让你不必每次查询都扫描全部数据,而是能像查字典一样,快速定位到最相关的那些向量。
如果把暴力搜索比作"在图书馆里一本一本地翻书",那么:
- IVF索引 = 先把书按类别分到不同书架,你直接去"历史区"找,不用逛遍全馆
- HNSW索引 = 图书馆里修了一条条"快速通道",让你能沿着路标跳跃式直达目标
这两种都是近似最近邻搜索(ANN)算法,通过牺牲微乎其微的精度,换来几十上百倍的速度提升。
一、IVF索引:先聚类,再搜索
IVF的核心思想是借鉴了文本检索里的"倒排索引"------把相似的向量预先分到同一个"桶"里。
构建过程(离线阶段)
| 步骤 | 做什么 | 类比 |
|---|---|---|
| 1. 聚类训练 | 用 K-Means 算法把数据集分成 nlist 个簇,每个簇有一个中心点 |
决定图书馆分几个区 |
| 2. 建立倒排表 | 把每条向量分配到离它最近的中心点所属的簇 | 把书放到对应书架上 |
| 3. 压缩编码(可选) | 对每个簇内的向量做压缩,如PQ(乘积量化)、SQ8(标量量化) | 给书做索引卡 |
构建完成后,你会得到一个质心表 + 多个倒排列表的结构。
查询过程(在线阶段)
用户查询向量 q
│
▼
1. 计算 q 到所有质心的距离,排序
│
▼
2. 选择距离最近的 nprobe 个簇(只在这几个桶里搜)
│
▼
3. 在这些簇内做精确/近似距离计算
│
▼
返回 Top-K 结果
这个过程就像一个高效图书馆员:读者进门后,先判断"你要的书可能在历史区或文学区",然后只在这两个区域里仔细找,不用逛遍全馆。
IVF 的三种常见变体
| 变体 | 特点 | 内存占用 | 召回率 | 适用场景 |
|---|---|---|---|---|
| IVF_FLAT | 无压缩,存储原始向量 | 高 | 最高(95%+) | 中等数据量,精度优先 |
| IVF_SQ8 | 标量量化压缩(4:1) | 中 | 高(90%+) | 大规模数据,平衡精度 |
| IVF_PQ | 乘积量化压缩(最高64:1) | 极低 | 中(70-90%) | 超大规模,内存紧张 |
二、HNSW索引:多层导航图
HNSW的全称是 Hierarchical Navigable Small World (分层可导航小世界)。它的核心是构建一个多层图结构:
- 最底层:包含所有数据点,连接最密集
- 往上层:每层只保留上一层的部分点,连接稀疏
- 最顶层:只有少量"地标点",连接最少
查询时,从上往下逐层导航:
- 从顶层开始,快速跳到离查询向量最近的区域
- 到下一层继续细化
- 直到最底层,找到最终的近邻
你可以把它想象成一个高速公路系统:在顶层走高速快速到达城市附近,下到地方道路后慢慢找具体地址。
HNSW 的关键参数
| 参数 | 含义 | 调参建议 |
|---|---|---|
| M | 每层每个节点的最大连接数 | 默认16,越大越准但越耗内存 |
| ef_construction | 构建时的候选列表大小 | 默认200,越大越准但构建越慢 |
| ef_search | 搜索时的候选列表大小 | 默认10,越大越准但查询越慢 |
三、IVF vs HNSW:全方位对比
| 对比维度 | IVF | HNSW |
|---|---|---|
| 核心思想 | 聚类分桶 | 多层图导航 |
| 内存占用 | 较低 | 较高(需将整个索引加载到内存) |
| 索引构建速度 | 快(只需聚类) | 慢(需建多层图) |
| 查询速度(无过滤) | 快���依赖 nprobe | 极快,对数级复杂度 |
| 查询速度(带过滤) | 好(质心层面先筛选) | 差(过滤比例高时几乎全图遍历) |
| 召回率 | 高(非量化可达95%+) | 更高(通常98%+) |
| 延迟分布 | 相对稳定,取决于簇大小 | 波动较大,受查询路径影响 |
| 是否需预训练 | 是(需K-Means聚类) | 否(可空表建索引) |
| 数据更新代价 | 较低(增量插入) | 较高(图结构需重构) |
四、如何选择?一张决策表
| 你的场景 | 推荐索引 | 原因 |
|---|---|---|
| 通用RAG,数据量百万级以内 | HNSW | 查询速度最快,召回率最高 |
| 数据量超大(亿级+),内存有限 | IVF_PQ | 压缩率高,内存可控 |
| 查询常带过滤条件(如按用户ID、时间范围) | IVF | 过滤场景下性能更稳定 |
| 追求极致的查询速度,内存充足 | HNSW | 对数级检索,毫秒响应 |
| 需要频繁插入新数据 | IVF | 增量更新代价更小 |
| 首次搭建,想快速验证 | IVF_FLAT | 构建快,参数少,效果好 |
数据量级参考建议
| 数据量 | 推荐方案 |
|---|---|
| < 10万 | 甚至不需要索引,暴力搜索即可 |
| 10万 ~ 100万 | IVF_FLAT 或 HNSW 均可 |
| 100万 ~ 1000万 | HNSW(内存充足)或 IVF_SQ8(内存有限) |
| > 1000万 | IVF_PQ(内存优先)或 IVF_SQ8(精度优先) |
五、关键参数调优
IVF 参数
| 参数 | 含义 | 调优经验 |
|---|---|---|
| nlist | 聚类数量(构建时固定) | 约等于 √N(N为向量数)。100万向量取1000左右 |
| nprobe | 搜索时探测的簇数(查询时可调) | 从1-16逐步试探,找到延迟与召回率的平衡点 |
HNSW 参数
| 参数 | 含义 | 调优经验 |
|---|---|---|
| M | 每层最大连接数 | 默认16,对精度要求高可调至32-64 |
| ef_construction | 构建时候选列表大小 | 默认200,越大越准但构建越慢 |
| ef_search | 查询时候选列表大小 | 默认40,越大召回率越高但延迟增加 |
六、使用示例
以下是在流行向量数据库中的配置方式:
Milvus / PyMilvus
python
# IVF_FLAT 索引
index_params = {
"index_type": "IVF_FLAT",
"metric_type": "COSINE",
"params": {"nlist": 1024}
}
# HNSW 索引
index_params = {
"index_type": "HNSW",
"metric_type": "COSINE",
"params": {"M": 16, "efConstruction": 200}
}
pgvector (PostgreSQL)
sql
-- HNSW 索引
CREATE INDEX ON items USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
-- IVFFlat 索引
CREATE INDEX ON items USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 1000);
-- 查询时设置 probes
SET ivfflat.probes = 10;
总结
IVF 像图书馆的"分类书架"------先确定在哪个区,再细找。
HNSW 像高速公路导航系统------从上到下,逐层精确定位。
- HNSW:查询更快、召回率更高,但内存占用大,构建慢
- IVF:内存友好、过滤场景稳定,构建快,但查询精度略低
对于绝大多数RAG应用(百万级向量、内存充足),HNSW通常是更好的起点 。如果数据量达到亿级或内存受限,则考虑IVF_PQ。
pgvector 是什么?
pgvector 是 PostgreSQL 的向量相似度搜索扩展。它的核心价值是:让你可以在关系型数据库里直接做向量检索,无需额外维护一套独立的向量数据库。
这意味着你的业务数据(用户表、订单表)和向量数据(文档 Embedding)可以放在同一个数据库里,用同一种 SQL 语法操作,还能享受 PostgreSQL 的 ACID 事务、时间点恢复、多表 JOIN 等企业级特性。
一、pgvector 支持哪些向量类型?
pgvector 支持 4 种向量类型,各有不同的存储开销和适用场景:
| 向量类型 | 存储开销 | 维度上限 | 适用场景 |
|---|---|---|---|
| vector(单精度) | 4 × 维度 + 8 字节 |
16,000 | 通用场景,精度最高 |
| halfvec(半精度) | 2 × 维度 + 8 字节 |
16,000 | 精度要求不高、希望节省内存 |
| bit(位向量) | 维度 / 8 + 8 字节 |
64,000 | 二进制向量(如哈希签名) |
| sparsevec(稀疏向量) | 8 × 非零元素数 + 16 字节 |
16,000 个非零元素 | 大部分元素为 0 的高维向量 |
实际建议 :大多数 RAG 场景直接用 vector 类型即��,维度通常在 384-1536 之间,存储开销完全可以接受。
二、pgvector 支持的距离度量
pgvector 提供了 6 种距离操作符,覆盖了主流相似度计算需求:
| 操作符 | 距离类型 | 说明 |
|---|---|---|
<-> |
欧几里得距离(L2) | 越小越相似 |
<=> |
余弦距离 | 越小越相似,相似度 = 1 - 余弦距离 |
<#> |
内积 | 返回负内积(因 PostgreSQL 只支持升序) |
<+> |
曼哈顿距离(L1) | 绝对值距离 |
<~> |
汉明距离 | 仅用于 bit 向量 |
<%> |
Jaccard 距离 | 仅用于 bit 向量 |
查询示例(找与查询向量最相似的 5 条记录):
sql
SELECT * FROM items
ORDER BY embedding <=> '[0.1, 0.2, ...]'
LIMIT 5;
三、支持的索引类型:HNSW vs IVFFlat
pgvector 支持两种近似最近邻(ANN)索引:HNSW 和 IVFFlat。两者的核心区别如下:
| 对比维度 | HNSW | IVFFlat |
|---|---|---|
| 工作原理 | 构建多层导航图 | 用 K-Means 把向量空间分成多个"桶" |
| 是否需要训练 | 否 | 是------需要先有代表性数据做聚类 |
| 索引构建速度 | 慢(需要建图) | 快(只需聚类 + 分配) |
| 查询速度 | 极快(对数级) | 较快 |
| 召回率 | 更高(98%+) | 较高(参数调优后可达 95%+) |
| 内存占用 | 较高(需存储整个图) | 较低(只存簇中心和倒排列表) |
| 对数据分布敏感度 | 低 | 高------数据变化后需重建索引 |
| 支持的最大索引维度 | ~2,000 | ~2,000(vector 类型) |
核心选择建议
| 场景 | 推荐索引 | 原因 |
|---|---|---|
| 追求查询速度和召回率,内存充足 | HNSW | 查询最快,召回率最高 |
| 向量频繁更新、内存有限 | IVFFlat | 构建快、内存小 |
| 百万级以内数据量 | HNSW | 简单,效果好 |
| 千万级以上、内存紧张 | IVFFlat + 半精度量化 | 压缩率高,内存可控 |
四、HNSW 索引的创建与参数调优
创建 HNSW 索引
sql
-- 使用 L2 距离
CREATE INDEX ON items USING hnsw (embedding vector_l2_ops)
WITH (m = 16, ef_construction = 64);
-- 使用余弦距离
CREATE INDEX ON items USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
关键参数
| 参数 | 含义 | 默认值 | 调优建议 |
|---|---|---|---|
| m | 每个节点的最大连接数 | 16 | 越大→图越密→召回率↑,但内存↑、构建↓ |
| ef_construction | 构建时的候选列表大小 | 64 | 越大→召回率↑,但构建时间显著↑ |
| ef_search | 查询时的候选列表大小(运行时设置) | 10 | 越大→召回率↑,但查询延迟↑ |
运行时调整 ef_search:
sql
SET hnsw.ef_search = 200; -- 查询前设置
SELECT * FROM items ORDER BY embedding <=> '[0.1, ...]' LIMIT 10;
五、IVFFlat 索引的创建与参数调优
创建 IVFFlat 索引
sql
CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops)
WITH (lists = 100);
关键参数
| 参数 | 含义 | 调优建议 |
|---|---|---|
| lists | 聚类中心数(构建时固定) | 约为 √(向量总数),100 万条取 1000 左右 |
| probes | 查询时搜索的簇数(运行时设置) | 越大→召回率↑,延迟↑。从 10 开始逐步测试 |
⚠️ 重要 :IVFFlat 索引必须先有数据再创建,因为需要通过 K-Means 学习数据的分布。
运行时调整 probes:
sql
SET ivfflat.probes = 10; -- 会话级别
SELECT * FROM items ORDER BY embedding <=> '[0.1, ...]' LIMIT 10;
六、pgvector 的核心优势与局限性
优势
| 优势 | 说明 |
|---|---|
| 与 PostgreSQL 无缝集成 | 向量数据可以和业务数据在同一张表、同一事务中操作 |
| 标准 SQL 语法 | 无需学习新 API,ORDER BY + LIMIT 即可检索 |
| ACID 事务 | 向量索引与表数据保持一致,支持回滚 |
| 企业级功能 | 时间点恢复、主从复制、多表 JOIN、行级安全 |
| 生态成熟 | 支持所有 PostgreSQL 客户端和 ORM 框架 |
| 开源免费 | 无额外许可费用 |
局限性
| 局限性 | 说明 | 影响程度 |
|---|---|---|
| 8KB 页面限制 | 向量过大时无法有效索引。建议索引维度 ≤ 2000 | 中 |
| 分布式能力弱 | 原生不支持水平分片,大规模需手动分片 | 高 |
| 过滤性能差 | 无法在向量检索时高效下推 metadata 过滤条件 | 中 |
| 索引类型有限 | 只有 HNSW 和 IVFFlat,缺少 DiskANN 等更先进的索引 | 中 |
| GPU 加速不支持 | 纯 CPU 计算 | 低 |
七、选择建议:pgvector vs 专用向量数据库
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 数据量 < 100 万,已在用 PostgreSQL | pgvector | 零额外运维成本,性能足够 |
| 需要 ACID 事务、多表 JOIN | pgvector | 专用向量数据库这些能力弱 |
| 不想引入新组件,降低架构复杂度 | pgvector | 一套数据库解决所有问题 |
| 数据量 > 500 万,查询 QPS 高 | 专用向量数据库(Milvus/Qdrant) | 分布式能力、GPU 加速 |
| 需要复杂的 metadata 过滤 | 专用向量数据库 | pgvector 过滤性能差 |
| 混合检索(向量 + 全文检索) | 两者结合 | pgvector 可结合 PostgreSQL 全文检索 |
快速开始(完整示例)
sql
-- 1. 安装扩展
CREATE EXTENSION IF NOT EXISTS vector;
-- 2. 创建表
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
title TEXT,
content TEXT,
embedding vector(384) -- 根据你的模型维度
);
-- 3. 创建 HNSW 索引
CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops);
-- 4. 插入数据
INSERT INTO documents (title, content, embedding)
VALUES ('年假政策', '公司年假每年15天', '[0.12, -0.34, ...]');
-- 5. 语义检索
SELECT title, content,
1 - (embedding <=> '[0.11, -0.33, ...]') AS similarity
FROM documents
ORDER BY embedding <=> '[0.11, -0.33, ...]'
LIMIT 5;
总结
pgvector 的核心价值是把 PostgreSQL 变成"能理解语义的关系数据库"。它让向量检索不再是独立系统的专利,而是关系型数据库的一个自然扩展。
对于大多数 RAG 应用(数据量百万级以内),pgvector 是完全够用的选择,而且能极大简化架构。如果未来数据量暴增,再考虑迁移到专用向量数据库也不迟。
SELECT title, content,
1 - (embedding <=> '0.11, -0.33, ...') AS similarity
FROM documents
ORDER BY embedding <=> '0.11, -0.33, ...'
LIMIT 5;
---
## 总结
**pgvector 的核心价值是把 PostgreSQL 变成"能理解语义的关系数据库"**。它让向量检索不再是独立系统的专利,而是关系型数据库的一个自然扩展。
对于大多数 RAG 应用(数据量百万级以内),pgvector 是完全够用的选择,而且能极大简化架构。如果未来数据量暴增,再考虑迁移到专用向量数据库也不迟。