跳转到内容

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)搜索精度搜索速度
1010,000
1001,000
1000100
1000010很高很慢

经验公式lists = √N(数据量的平方根)是一个好的起点:

数据量 N推荐 lists
10K100
100K300~500
1M1000
10M3000~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 的局限

  1. 需要先有数据再建索引:空表建索引会导致聚类中心质量差
  2. 大量增量数据后需重建:新数据的分布可能与原始聚类中心不匹配
  3. probes 需要手动调优:不同数据分布的最优 probes 不同
  4. 不支持并行索引构建:大数据量建索引可能很慢

常见误区

误区 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 中更先进、更常用的向量索引。

基于 MIT 许可发布