主题
5.2 IVFFlat 索引
IVFFlat 把向量空间切成"格子"——搜索时只查附近的几个格子,跳过大部分数据
这一节在讲什么?
IVFFlat 是 pgvector 支持的两种向量索引之一。它的核心思想是"分而治之"——先把向量空间划分为 K 个区域(通过 K-Means 聚类),搜索时只扫描查询向量附近的几个区域,跳过大部分不相关的数据。这一节我们要理解 IVFFlat 的工作原理、关键参数 lists 和 probes 的调优方法、以及它的适用场景和局限。
IVFFlat 原理
IVFFlat 的工作分为两个阶段:
建索引阶段:对数据集做 K-Means 聚类,得到 K 个聚类中心(centroids),然后把每条向量分配到离它最近的聚类中心所属的区域(list)。索引存储的是每个区域包含的向量 ID 列表。
搜索阶段:计算查询向量与所有 K 个聚类中心的距离,选择最近的 probes 个区域,然后只在这些区域内做暴力搜索。
┌──────────────────────────────────────────────────────────────────┐
│ IVFFlat 索引结构 │
│ │
│ 聚类中心(lists=9): │
│ C1 C2 C3 │
│ C4 C5 C6 │
│ C7 C8 C9 │
│ │
│ 每个区域包含的向量: │
│ C1 → [v1, v5, v12, ...] │
│ C2 → [v3, v8, v15, ...] │
│ ... │
│ │
│ 搜索(probes=2): │
│ 查询向量 q 离 C2 和 C5 最近 │
│ → 只在 C2 和 C5 的向量中做暴力搜索 │
│ → 跳过了 C1, C3, C4, C6, C7, C8, C9 的所有向量 │
│ → 搜索量从 N 减少到约 2N/9 │
└──────────────────────────────────────────────────────────────────┘创建 IVFFlat 索引
sql
-- 创建 IVFFlat 索引(余弦距离)
CREATE INDEX idx_docs_embedding_ivf ON documents
USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 100);
-- 创建 IVFFlat 索引(L2 距离)
CREATE INDEX idx_docs_embedding_l2 ON documents
USING ivfflat (embedding vector_l2_ops)
WITH (lists = 100);重要:IVFFlat 索引需要在表中已有一定量的数据后再创建——因为聚类中心需要基于数据分布来计算。如果空表建索引,聚类中心是随机的,搜索质量会很差。建议至少有 1000 条数据后再建 IVFFlat 索引。
lists 参数调优
lists 决定了向量空间被划分为多少个区域。它的选择直接影响搜索的精度和速度:
| lists 值 | 每个区域的向量数(假设 N=100K) | 搜索精度 | 搜索速度 |
|---|---|---|---|
| 10 | 10,000 | 低 | 快 |
| 100 | 1,000 | 中 | 中 |
| 1000 | 100 | 高 | 慢 |
| 10000 | 10 | 很高 | 很慢 |
经验公式:lists = √N(数据量的平方根)是一个好的起点:
| 数据量 N | 推荐 lists |
|---|---|
| 10K | 100 |
| 100K | 300~500 |
| 1M | 1000 |
| 10M | 3000~5000 |
probes 参数调优
probes 决定搜索时扫描多少个区域。它通过 SET 命令在查询前设置:
sql
-- 设置 probes(默认为 1)
SET ivfflat.probes = 10;
-- 然后执行查询
SELECT id, content, embedding <=> '[0.1, ...]' AS distance
FROM documents
ORDER BY embedding <=> '[0.1, ...]'
LIMIT 5;probes 与 lists 的关系:
| probes / lists | 搜索范围 | 召回率 | 速度 |
|---|---|---|---|
| 1/100 (1%) | 极小 | 低 | 最快 |
| 10/100 (10%) | 小 | 中高 | 快 |
| 50/100 (50%) | 大 | 高 | 中等 |
| 100/100 (100%) | 全部 | 100% | 等于暴力搜索 |
推荐:probes = lists 的 1%~10%。比如 lists=100 时,probes=5~10 通常能获得 95%+ 的召回率。
IVFFlat 的局限
- 需要先有数据再建索引:空表建索引会导致聚类中心质量差
- 大量增量数据后需重建:新数据的分布可能与原始聚类中心不匹配
- probes 需要手动调优:不同数据分布的最优 probes 不同
- 不支持并行索引构建:大数据量建索引可能很慢
常见误区
误区 1:空表建 IVFFlat 索引
空表建索引时,聚类中心是随机初始化的,搜索质量极差。正确做法是先插入数据,再建索引:
sql
-- ❌ 错误:空表建索引
CREATE TABLE documents (...);
CREATE INDEX ... USING ivfflat ...; -- 聚类中心是随机的!
-- ✅ 正确:先插入数据再建索引
INSERT INTO documents ...; -- 插入至少 1000 条数据
CREATE INDEX ... USING ivfflat ...; -- 基于真实数据分布的聚类误区 2:lists 设得越大越好
lists 过大导致每个区域的向量太少,搜索时需要扫描更多区域才能找到足够的结果,反而降低性能。
误区 3:probes 设得越高越好
probes 过高接近暴力搜索,失去了索引的意义。找到"刚好够"的 probes 值是调优的目标。
本章小结
IVFFlat 通过 K-Means 聚类把向量空间划分为多个区域,搜索时只扫描查询向量附近的几个区域。核心要点回顾:第一,lists 参数推荐 √N,probes 参数推荐 lists 的 1%~10%;第二,IVFFlat 需要先有数据再建索引,空表建索引会导致聚类中心质量差;第三,大量增量数据后需要重建索引;第四,probes 调优需要在召回率和速度之间找到平衡点。
下一节我们将学习 HNSW 索引——它是 pgvector 中更先进、更常用的向量索引。